Supply-chain case study
Patched · released 21 Oct 2026, 14:00 UTCOn 20 October 2026 the PHP security team gave one day's notice of an emergency release for two critical vulnerabilities, CVE-2026-109618 and CVE-2026-109619, each rated CVSS 4.0 9.2 and exploitable without authentication. Then the details leaked ahead of the coordinated disclosure. Every PHP release since 5.4 is in scope, and Sansec expected mass exploitation within fifteen minutes of the fix going public. The fix shipped at 14:00 UTC as PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35; Cloudflare reported early attacks the same day. Here is what the advisory says, what it means for the PHP you don't know you run — and, honestly, what a registry can and cannot do about a runtime CVE.
What is confirmed
The confirmed facts are few and all of them are bad. Two remote-code-execution flaws in the PHP interpreter itself, each CVSS 4.0 9.2, both exploitable without authentication according to CERT-EU. The PHP security team's own notice reserved the right to release early "should details of either vulnerability become public, or should evidence of active exploitation appear in the wild" — and the details did become public, which is why the name PHP2Shell exists at all. WatchTowr reproduced CVE-2026-109618 and confirmed the vulnerabilities "are indeed real" while withholding technical specifics until the patch. The advisory published with the release describes two distinct bugs in request lifecycle handling — one triggered by out-of-memory conditions, one by state that outlives its memory — detailed below.
What makes this a supply-chain story rather than a web-app
story is the shape of the affected population. PHP is not a
dependency you declare; it is a runtime you inherit. It arrives
inside php:* and wordpress:* container
images, inside distribution packages, inside managed-hosting
stacks, and inside commercial products that embed it without
saying so. JFrog's own guidance on this CVE makes the point
bluntly: a dependency inventory is not enough, and vendors "may
backport security fixes without changing the upstream version
number" — so a scanner that matches upstream version strings
will flag patched systems as vulnerable and, worse, has no way
to know about runtimes it never saw.
Before the release neither WatchTowr nor Huntress had observed exploitation in the wild; by the afternoon of 21 October, Cloudflare's CTO reported "early attacks emerging", and exploit code of unverified provenance was circulating publicly. The window was, as predicted, measured in hours. The Log4Shell precedent is instructive in the wrong way: a critical, trivially triggered flaw in a ubiquitous runtime became what the Cyber Safety Review Board called an "endemic vulnerability", still being exploited years later because the hard part was never the patch — it was finding every place the vulnerable component lived.
Fixed versions & root cause — from the 21 Oct release
Fixed in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35.
PHP 8.1 and older receive no updates from the project; the
security team published minimal source backports for 7.4, 8.0
and 8.1, and distributions and LTS vendors may ship their own.
The volunteer rpms.remirepo.net repository had no
updates on release day.
CVE-2026-109618 — RCE via OOM corruption. Out-of-memory handling can interrupt PHP while internal request data is only partially initialised; request cleanup then treats the invalid data as valid. CVE-2026-109619 — RCE via stale global state. Request-owned internal state can survive after its backing memory has been freed, and a later request on the same worker may use it before normal initialisation resets it.
At risk: every PHP
installation serving web requests — PHP-FPM, Apache with
mod_php, php-cgi, FrankenPHP, LiteSpeed. The project rates
mod_php highest, with a demonstrated unauthenticated exploit
that fires before any application code runs. Partial
mitigations only (memory_limit=-1 with a small
post_max_size for 109618;
session.upload_progress.enabled=0 for 109619,
which "blocks the confirmed session paths but not other
variants") — none of them makes an installation safe.
2 × 9.2
critical, unauthenticated RCEs in the PHP interpreter (CVSS 4.0)
5.4+
every PHP release since 2012 in scope; 8.1 and older get source backports only
24 h
from advance notice to release — and the details leaked inside that window
15 min
Sansec's estimate from patch publication to mass exploitation
The OrbitalReg defence
JFrog's "inventory every runtime" advice is a scramble if your images were pulled from Docker Hub by whoever needed them. If they flowed through your registry, the SBOM index already answers "which images contain a PHP runtime, which version, pulled by whom" — including the PHP inside a vendor appliance image nobody tagged as PHP.
The moment the CVEs are in the feeds with affected versions, a
policy on critical CVEs makes every pull of a vulnerable
php base image or package return
403 — so no new
deployment, rebuild or autoscaling event ships the hole while
you patch, and patched tags flow through untouched.
The backport problem is exactly why OrbitalReg scans images with Trivy and Grype, which read distribution security trackers rather than comparing upstream version strings. A Debian package that carries the fix under an unchanged version number is recognised as fixed; a scanner that only knows "8.3.x < fixed" is wrong in both directions.
Log4Shell went endemic because nobody could enumerate the exposure. The append-only pull log is that enumeration, kept for you: every image and package that contained the affected runtime, who pulled it, when, and whether it has since been pulled again as a fixed version.
Timeline & identifiers
Compiled from the PHP security advisory and release notes, JFrog Security Research, SecurityWeek, Sansec and Tenable reporting of 20–21 Oct 2026. Exploitation is ongoing; cross-check the sources below before acting.
Sources
Do you know every image that carries a PHP runtime?
Rico, the founder, walks through SBOM indexing, critical-CVE pull-gates and the audit trail for container images and distro packages — the controls that turn "inventory everything" from a scramble into a query.