Most of the PHP I’ve shipped has sat behind something else: a reverse proxy, a queue worker, a WordPress install somebody else owned. That is why PHP2Shell got my attention. The bug is not in a framework you can pin or a plugin you can disable. It is in the interpreter that runs before any of your code gets a say.
On October 21, the PHP project released 8.5.12, 8.4.27, 8.3.36 and 8.2.35, fixing two critical remote code execution flaws tracked as CVE-2026-109618 and CVE-2026-109619. Both are rated 9.2 under CVSS v4.0. The internet nicknamed the pair PHP2Shell, and the story got told as a leak story: somebody broke an embargo, maintainers scrambled, everyone patched in a panic.
That framing buries the useful part. The PHP Foundation’s own writeup says the root cause was found in 2024, and that the fix stalled for two years inside a security team that could not agree on a trade-off. The leak is a distraction. The two-year gap is the incident.
🐘 Two Bugs in the Parts Every Request Touches
Neither flaw lives in userland. Both sit in PHP core, in memory management and error handling, which is why they reach anything that serves a request.
CVE-2026-109618 (GHSA-w8hw-w9fx-j79w) is an out-of-memory corruption bug. PHP aborts a request that hits memory_limit with longjmp(), which skips normal control flow. If the abort fires while internal request data is only partially initialized, cleanup later treats that garbage as valid and follows a pointer the attacker can shape. On Apache with mod_php, an unauthenticated request can trigger this before application code runs, and the PHP Foundation says full remote code execution was demonstrated with default settings. On PHP-FPM, execution was confirmed through application-level code paths. Tanium ties the OOM path specifically to multipart parsing.
CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp) is a stale global state bug. A fatal error calls zend_bailout(), again a longjmp(), and request-owned pointers can survive in global memory after the allocation behind them is freed. Workers handle requests in sequence, so the next request on that worker can read freed memory before re-initialization clears it. The reporter confirmed a use-after-free, a worker crash, and code execution against a real application on mod_php.
Zscaler describes the two as chainable: 109618 lets an attacker shape the freed contents, and 109619 lets the next request consume them. No authentication, no user interaction, no framework in the way.
⏳ The Root Cause Was Found in 2024
Here is the timeline that should worry you more than the leak.
In 2024, PHP went through its first external audit in years, funded by the Sovereign Tech Agency, organized through OSTIF and run by Quarkslab. While fixing one finding, a core developer contracted by the Foundation found the first signs of the out-of-memory problem. An exploit was theorized that year, but nobody found a concrete vector, and issues with clearer impact took priority.
Fixing it properly meant choosing between backward compatibility breaks, new denial-of-service potential, or forcing changes on a large ecosystem of third-party extensions. The security team operates on consensus. The Foundation does not soften what happened next: the team struggled to choose, feared shipping the wrong option, and work on the fix “stalled and restarted repeatedly over months.” It calls the delay a structural failure of both the decision process and its resources.
What broke the deadlock was outside research. Under OpenAI’s Patch the Planet program, the security firm Calif started auditing PHP on July 30, 2026. On August 11, researcher Ryan A reported an unconditional remote code execution with the same root cause the project had found two years earlier. OpenAI independently reported that root cause and followed with more attack vectors on September 18. Wiz reported additional vectors on August 24. Trail of Bits has also been auditing the codebase. Only then did it become the top priority for everyone the Foundation had on security.
Detection got cheap and fast: AI-assisted scans, exploit validation tooling, sanitizer builds, several firms auditing the same codebase. What did not get fast was the decision about which fix to ship. The bottleneck moved from finding bugs to choosing a patch, and the project lacked senior people to make that choice quickly.
📣 The Embargo Broke, and It Was Never Your Protection
On October 20 around 15:00 UTC, the two CVE IDs started circulating on forums and social media with a wrong release time and a wrong CVSS score of 10.0. The real score is 9.2, and no technical details leaked then. PHP moved its pre-announcement to 19:30 UTC and shifted the release from October 22 at 14:00 UTC to October 21 at 14:00 UTC, a reasonable compromise while distributors were still packaging.
Around 20:00 UTC, Calif mistakenly published a proof of concept video showing the tool working against WordPress, Drupal, Joomla, Magento and others. It was deleted, copies spread anyway, and it accelerated third-party proof of concept work. Then on October 21 at 07:41 UTC, more than six hours before the coordinated release, Chainguard published built images containing the fix. The Foundation is blunt: that disregarded weeks of work by many parties.
The detail that reframes the leak debate comes from AWS. Amazon Threat Intelligence found a PHP2Shell exploitation attempt against its MadPot honeypot dated October 17, four days before public disclosure, from a commercial anonymization network. AWS says it is unclear how the actor got exploit information that early. Whatever the answer, the embargo was not the thing standing between attackers and your servers.
The apology thread on Hacker News is a sideshow. A person repeated a rumor he had been told was not under embargo, through a coordination graph with too many links and no shared source of truth. Embargoes assume a small trusted set and a vendor with enough staff to decide. Neither is a control you can put in a runbook.
🧯 What I’d Actually Do This Week
One correction on the hype, because it changes how you prioritize. VulnCheck reproduced both flaws and found code execution works on ARM64 and on x86 with ASLR disabled. Dozens of public proof of concepts exist, but very few work, and none bypass ASLR on x86-64. Red Hat rates RCE as hard on default PHP-FPM deployments. This is not a universal one-request root shell.
It is still a 9.2, because the mod_php pre-auth path, the credential-bearing memory disclosure, and the chained variant are real. Internet-facing mod_php goes first, then ARM64 hosts, then FPM.
Stop trusting package inventory. PHP hides in OS packages, Homebrew, Windows zip folders, XAMPP stacks, vendor appliances, container tags, and laptops. Tanium’s approach is right: read the version string out of the binary instead of asking a package manager. An SBOM covering only OS packages quietly lies to you.
Patch to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, then restart PHP-FPM and the web server so the new binary loads. Source backports exist for 7.4, 8.0 and 8.1 with no official releases, so treat them as a stopgap. Zscaler puts every version from 5.4 onward in the vulnerable set, and only 8.2 and later got official patches. On 8.1 or older, you are on your own.
Two smaller things for the change ticket. session.upload_progress.enabled=0 closes one demonstrated path for 109619 and does nothing for 109618; it has to go in php.ini or the FPM pool config with a worker restart, not ini_set(). And measure patch latency: pull your last twenty emergency advisories and compute the median hours from publication to production. That number is your real exposure, not the CVSS score. Censys counted more than 13 million internet-facing assets on possibly affected PHP versions.
💸 The Bottleneck Is People, Not Bugs
The uncomfortable part of the Foundation post is the staffing. The Ecosystem Security Team started in May 2026, Alexandre Daubois joined in July to triage and fix reports, and Daniel Scherzer joined in September for hardening. Since July 1 the team has published 15 CVEs and worked through more than 100 invalid, duplicate or unreproducible reports.
That is the shape of modern vulnerability work. AI tools generate report volume faster than a handful of contractors can triage it. The Foundation’s line is the one to remember: tools cannot decide on the right fix for a long-standing engine design decision, weigh compatibility tradeoffs, or review the patch.
The money question is not abstract. If your revenue runs through PHP, you depend on a few funded developers and some volunteers to make architectural security decisions for you. The 2024 audit that eventually surfaced this bug was paid for by a German government agency, not by the hosting companies and commerce platforms that ship PHP to millions of users. Sponsoring the Foundation is cheaper than a week of incident response, and it shortens the next two-year stall.
🧠 Final Thoughts
PHP2Shell will be remembered as the leak that broke the embargo. The bug was identified in 2024, sat through months of unresolved trade-off debates, and only got fixed after outside AI-assisted audits made the impact impossible to defer. Then the disclosure plan failed anyway, in a way that proved secrecy was never what protected anyone.
The scarce resource is no longer bug discovery. It is senior engineers with the authority and time to decide, plus the funding to keep them. Your equivalent is patch latency, so measure it before the next 9.2 lands. Check what you are running tonight, and if your business depends on PHP, look at what you have given back.
Thanks for reading.