PHP Knew About This Bug for Two Years. Three Labs Built Working Exploits in Nine Days.
The release landed at 14:00 UTC on 21 October. By then the fix had been sitting in public container images for six hours and nineteen minutes. That gap matters more than the score.
Two memory-safety flaws in the PHP runtime, tracked as CVE-2026-109618 and CVE-2026-109619 and collectively called PHP2Shell, let an unauthenticated attacker run code on a web server. Both score 9.2 under CVSS v4.0. Both sit in the parts of PHP that nearly every request touches: memory management and error handling. Neither is a bug in WordPress or Drupal or Laravel: a stock "Hello World" on a vulnerable runtime is in scope.
The patched releases are PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Everything from 5.4 through 8.5.11 is affected by at least one of the two, according to Wiz Research.
What actually goes wrong
PHP has to survive its own failures. When a request dies partway through, the engine still runs cleanup, and cleanup assumes the data it walks is well formed.
The first flaw breaks that assumption at the worst moment. PHP is building the $_POST array and increments the hash table's entry counters before it allocates a copy of the field name. If the allocation trips memory_limit, it raises a fatal error and abandons the insertion with the counters already advanced. The bucket is counted but never initialised. Cleanup visits it, treats whatever bytes are sitting there as a PHP value, and follows them.
Datadog's security team reproduced this. An unauthenticated POST, a field name around 3.5 megabytes, heap grooming to plant controlled bytes in the abandoned bucket, and a destructor callback pointed at system(). Their exploit returns HTTP 200 with the memory error in the body and finishes in 3.10 seconds. The Apache worker is still running. The command executes with the worker's privileges.
On Apache with mod_php all of it happens during request initialisation. As Datadog put it: "Nothing in the target script can impact or defend against the exploit (authentication, filtering)."
The second flaw is a use-after-free with a longer reach: a fatal error leaves a worker holding a pointer into memory from a previous request, and the next request on that worker can follow it. Wiz turned one variant into a memory disclosure that URL-encodes the reclaimed heap into a PHPSESSID response cookie. An attacker reads passwords, tokens and API keys out of a PHP worker without executing anything, and it works when the application never calls session_start(). Nothing spawns a shell, so nothing appears in the process tree.
Two years, and then nine days
The delay is the part that transfers to other software.
PHP's maintainers saw this shape in 2024. During the project's first external security audit in years, organised through OSTIF and funded by Germany's Sovereign Tech Agency, a core developer working through a Quarkslab finding noticed the first signs of the out-of-memory problem. An exploit was theorised. No concrete vector was found. Other bugs with clearer impact took priority.
Then the fix failed for two years in an ordinary way. Every proposed approach carried a real cost: breaking backward compatibility, creating a denial-of-service surface, or forcing coordinated changes across a large ecosystem of third-party extensions. The security team ran on consensus. The number of people with deep enough knowledge of the engine to review the work was small and busy elsewhere. The effort stalled, restarted, stalled again.
The PHP Foundation did not soften this. "We won't pretend otherwise: the delay was a structural failure, caused by both the security team's decision process and its lack of resources."
On 11 August 2026, a researcher at Calif named Ryan A reported an unconditional remote code execution with that same root cause. OpenAI reported the same root cause independently, the same day. Wiz found its first PHP bug on 19 August, confirmed unauthenticated RCE against Drupal on the 20th and WordPress on the 21st, and reported on the 24th. OpenAI sent further vectors on 18 September. PHP acknowledged everything on 21 September and began building a coordinated fix.
A flaw understood in outline for two years became unignorable in nine days. The 2024 problem was never that nobody could see the bug. It was that nobody could afford to prove what it was worth. Reachability sets severity, severity sets priority, and proving reachability on a memory-corruption bug across multiple PHP versions and configurations used to cost a specialist team months.
Wiz's summary: "With frontier AI models and the right tools, a single researcher completed in a week what previously would have taken a world-class team months of work." They credit Wiz Atlas, Gemini 4 Argon and GPT-5.6 Sol, with Argon doing particular work on exploit development and patch validation.
OpenAI's framing is blunter and, for once, optimistic. "To our knowledge, frontier AI found these vulnerabilities before attackers did." It calls the interval that bought them a Defender's Window, and has put $1 billion into subsidised model access for under-resourced defenders.
The patch is a recipe
That window closed early, and not because of an attacker.
The release was set for 22 October. On 20 October at around 15:00 UTC both CVE identifiers started circulating on forums and social media, attached to a wrong release time and a wrong CVSS score of 10.0. No technical detail came with them, but PHP moved its pre-announcement to 19:30 UTC and pulled the release to 21 October. Around 20:00 UTC, Calif accidentally published a video of its proof of concept working against live targets. Copies spread. Mitigation advice sent to a handful of critical infrastructure providers the previous night may also have leaked. The Foundation has not said who leaked the CVE identifiers.
Then, at 07:41 UTC on 21 October, Chainguard published built images containing the fix, more than six hours early and open to anyone, not just customers.
The Foundation's objection is precise: "Any attacker could now study the fix in binary form while every other PHP user was still waiting for the release."
A patched binary is a diffable binary. That is the entire reason embargoes exist, and Chainguard's own chief executive has made the argument better than the Foundation did. Dan Lorenc, in a July essay: "Pre-disclosure protection extends exactly as far as the people you can vet and hold to an embargo."
He does not dispute the timeline, only the framing. "We didn't leak," he told The Register. "It's coordinated vulnerability disclosure, unless there's evidence of active exploits in the wild."
The two accounts genuinely diverge here, and nobody has published the evidence to settle it. Lorenc says several vendors on the disclosure call reported exploitation, and that Chainguard's honeypots caught IPs from China and Russia hitting the bugs on Wednesday. Cloudflare says it observed attack traffic and watched attackers mutate payloads to probe the rule logic. AWS has reported exploitation attempts as early as 17 October, four days before disclosure.
The PHP security team says something narrower: it inspected the proof-of-concept code circulating after the leak and "did not identify any functional exploits at the time of release/public disclosure."
Both can be true. Attempts are not compromises, and no one has published a confirmed one. The asymmetry matters for how you read every "no evidence of exploitation" statement this week. It describes the evidence, not the servers.
What the estate actually looks like
PHP is used by 69.3% of websites whose server-side language can be identified, per W3Techs. Note the denominator. The widely repeated "70% of the web" is that figure with the qualifier sanded off.
Inside that base, PHP 7 is 26.9% and PHP 5 another 7.7%. PHP 8.0 and 8.1, both past end of life, account for roughly 14% of PHP's own footprint. Close to half of PHP sites therefore run branches the project no longer supports. PHP 5 gets nothing at all this time. The source backports cover 7.4, 8.0 and 8.1, and someone still has to compile and deploy them.
VirusTotal puts more than 5.1 million PHP hosts on the public internet, about 3.8 million with at least one port open.
The inventory problem is worse than the version numbers suggest. The php -v on the box is often not the runtime answering requests: containers bundle their own interpreter, and distributions backport fixes without changing the version string. Google's own guidance makes the point in passing, telling Cloud Run and GKE users to rebuild images rather than assume a base image moved. Meanwhile mod_php, the configuration carrying the worst risk, is the one that survives longest, because it is the one nobody wants to re-platform.
Detection is weak by design. The exploit does not have to crash anything, and Datadog's runs return a 200. So the useful instruction is not "check whether you were hit." It is: patch, restart every worker, and rotate the credentials those workers could see. The memory-disclosure path needs no command to run on your box.
What changed this month
PHP being buggy is not news. What changed is the shape of the interval between a bug existing and a bug being a weapon.
On one side it compressed to days: three independent teams, working with the same class of models, converged on a two-year-old problem inside nine days. On the other it compressed to hours, which is why a vendor decided it could not wait six more of them. Meanwhile the interval that protects you, the time to inventory a runtime, get a change through a maintenance window and restart the workers, is still measured in quarters. That mismatch is the whole exposure, and it is not a PHP problem. It is the same mismatch that turns up in firmware, in bootloaders and in every appliance with a web console that shipped in 2019 and has not been touched since.
For a company that makes a connected product, the PHP is almost never the product. It is the dealer portal, the configurator, the warranty lookup, the supplier's extranet an agency built and handed over with a document nobody read. At DMC we spend most of our time on the physical side of product companies, on sourcing and cost models and ramp plans. The recurring lesson there is that the dependency missing from the diagram is the one that stops the line. Software estates behave the same way.
The question worth answering this week is which runtime answers requests for that portal, and who is allowed to restart it. If that takes a meeting, the gap is the finding. let's talk