Patch Lag Explains the Old Breaches. It Does Not Explain the New Ones.
Reviewing the major incidents of the last two decades side by side, they split cleanly into two groups with different root causes, and the balance between those groups has shifted. Security programmes built entirely around the first group are underprepared for the second.
This post is for people who own vulnerability management and are deciding where the next unit of effort goes.
Group one: the patch existed, and was not applied
For several of the most damaging incidents on record, a vendor patch was publicly available well before the damage occurred.
| Incident | Patch available | Damage began | Gap |
|---|---|---|---|
| SQL Slammer | MS02-039, July 2002 | 25 January 2003 | ~6 months |
| WannaCry | MS17-010, 14 March 2017 | 12 May 2017 | ~2 months |
| Equifax | Struts fix for CVE-2017-5638, 7 March 2017 | Intrusion from mid-May 2017 | ~2 months |
In all three, the attacker's technical sophistication was not the determining variable. Availability of an unapplied patch was. This is the failure mode that vulnerability scanning, patch SLAs and asset inventory were built to address, and against this group they work.
See the individual teardowns for SQL Slammer, WannaCry and Equifax.
Group two: there was nothing to patch
A second group does not fit that model at all, because at the moment of compromise no fix existed, and in several cases no vulnerability existed either.
- SolarWinds (2020) and 3CX (2023): the delivered software was authentic, current and validly signed. Patching faster would have delivered the backdoor faster.
- event-stream (2018) and XZ Utils (2024): malicious code was introduced by someone holding legitimate publishing rights. The package had no CVE history to flag.
- Dependency confusion (2021): no package was vulnerable. Resolution order was ambiguous, and an attacker could win it by publishing to a namespace the victim did not control.
- polyfill.io (2024): the entry point was a domain sale.
A scanner comparing installed versions against a vulnerability database returns nothing for any of these, correctly, because the version installed was the version intended.
The honest counter-argument
Group one is not historical. ProxyLogon (2021) saw mass exploitation of unpatched servers within days of disclosure, and that pattern recurs with every widely-deployed internet-facing product. Patch velocity still matters enormously.
It is also worth being precise about Log4Shell, which is often cited as a patch-lag case and largely was not: exploitation and the 2.15.0 release both landed around 9-10 December 2021. The hard part was never patch availability. It was that Log4j arrived transitively, so organisations could not answer whether they were affected. That is an inventory problem wearing a patching problem's clothes.
What follows
The two groups need different controls, and the second group's controls are the less mature ones in most programmes:
- Group one wants speed: accurate inventory, fast patch SLAs for internet-facing systems, prioritisation weighted by known exploitation.
- Group two wants provenance: knowing what a component is built from and who can publish it, integrity verification that does not stop at a valid signature, pinned and verified external scripts, and explicit registry resolution.
The practical bridge between them is the same artefact: knowing precisely what you run, including transitively. That answers Log4Shell's actual question, and it is the prerequisite for noticing that a component's publisher changed.
None of this is an argument for patching less. It is an argument that a programme scoring itself only on mean-time-to-patch is measuring its readiness for the first group and saying nothing about the second.
Method
Dates above are from the vendor advisories and public incident reports linked in each teardown. Where the public record is ambiguous about a first-exploitation date, the table uses the conservative figure, so real gaps may be longer than shown.