In 2024, during PHP’s first external security audit in years — organised by the PHP Foundation with funding from the Sovereign Tech Agency, arranged through OSTIF and carried out by Quarkslab — a PHP core developer contracted by the Foundation found the first signs of an out-of-memory problem in the engine. It was not hidden in an obscure extension. It sat in the code that decides what happens when a request dies badly, the path every website on the planet can reach. The audit was not the end of the story; it was the beginning of a delay that the Foundation now describes in plain words.
The team developed several competing approaches to the fix, and each came with its own cost: breaks in backward compatibility, new potential for denial of service, or changes across many third-party extensions that seemed impossible to coordinate. The security team works by consensus, struggled to choose between them, and feared the consequences of shipping the wrong one. Add a small core team, and add one more factor that sounded decisive at the time: no known path to remote code execution. Work on the fix stalled and restarted over months. The Foundation does not soften the verdict — the delay, it writes, was a structural failure caused both by the decision process and by a lack of resources, and the security team has since begun changing how it decides [3].
That version is less comfortable than a story about one bad call, because there is no single person to blame and no single moment to point at. It is also the version that explains what happened later. The Foundation lists the missing route to code execution as one reason among several and does not rank them; what matters for everything that follows is that the absence of a known path made the trade-offs look less urgent than they were.
On 21 October 2026, PHP shipped emergency releases: 8.2.35, 8.3.36, 8.4.27 and 8.5.12, together with source patches for the end-of-life branches 7.4, 8.0 and 8.1, for which no official binaries exist [2]. The two vulnerabilities behind the release are tracked as CVE-2026-109618 [5] and CVE-2026-109619 [6]; the pair has been given a name, PHP2Shell [1]. PHP rates them 9.2 on CVSS 4.0 [3].
What the two bugs do is unglamorous, which is why it is easy to underestimate them. The first is a use-after-free in the unserializer that handles upload progress: an unauthenticated attacker can read a PHP worker’s memory and, in the demonstrated chains, corrupt process memory and execute code. The second is a use-after-free in the fatal-error backtrace combined with a type confusion in OPcache, which corrupts shared PHP state and executes code as well [1]. Both grow from the same root. When a request ends in a fatal error — an out-of-memory condition is the simplest way to force one — PHP can leave a corrupted internal state behind, and a later request, or a worker that has been recycled, picks that stale state up and reuses it. The attacker never needs a flaw in your application; the vulnerable code is the interpreter itself.
The practical reach depends on how PHP is deployed, and this is where the details stop being academic. On Apache with mod_php, an unauthenticated request can trigger the flaw before any application code runs, and full remote code execution was demonstrated with default settings. On PHP-FPM, remote code execution was confirmed through application-level code paths [3]. The affected range runs from PHP 5.4, released in 2012, to 8.5 — every version of the last decade is affected by at least one of the two bugs [1]. Wiz, a commercial cloud-security vendor, puts the share of the world’s websites running PHP at roughly seventy percent, and the demonstrated impact reads like the worst kind of security bulletin: disclosure of a worker’s memory, the place where passwords, session tokens, API keys and certificates belonging to other requests live, and pre-authentication code execution on WordPress, Drupal and Joomla [1]. Not because those applications were broken. Because they were running on the interpreter.
The discovery has its own timeline, and it is shorter than the delay before it. The PHP Foundation brought PHP into the Patch the Planet programme, under which the security firm Calif began auditing PHP on 30 July 2026 [3]. On 11 August, a researcher at Calif reported an unconditional remote code execution with the same root cause as the out-of-memory problem found in 2024 [8]. OpenAI reported the same root cause independently [7]. Wiz Research found it separately, using Wiz Atlas, Gemini 4 Argon and GPT-5.6 Sol, and confirmed code execution against Drupal on 20 August [1][3]. The patch arrived on 21 October: roughly two months from the first report, not two days.
That gap between reporting and patching is the part of the story that generalises. In 2024, the note in the file said that demonstrating exploitability would require an exceptionally complex chain of steps, at the edge of what human researchers could practically achieve — a sentence written in 2026, looking back, not a verdict from the 2024 debate [1]. What changed in the years between is not the bug. It is the cost of turning a bug into a working weapon for people with the right skills. In Wiz’s account, frontier models allowed working exploits to be developed and adapted across multiple applications in hours [1]. The Foundation, for its part, states that it could not identify any functional exploits at the time of release [3]. Both statements describe different things: a demonstrated capability in the hands of researchers, and the state of the proof-of-concept landscape at the moment the patches shipped. What the research changed is the barrier, not the crime statistics.
Then the coordination itself cracked, which is the part almost nobody plans for. The coordinated disclosure worked until two days before the release date. On Tuesday, 20 October, at around 15:00 UTC, the two CVE identifiers began circulating on forums and social media — accompanied by a wrong release time and a wrong CVSS score of 10.0, where the actual score is 9.2. No technical details leaked in that first wave. The project brought its pre-announcement forward and moved the release from 22 October to 21 October. That evening, one of the firms that had reported the issues, Calif, mistakenly published a video showing its proof of concept working against a number of targets; copies circulated after the video was deleted, and the Foundation says it contributed to accelerated development of further proof-of-concept code. On 21 October at 07:41 UTC — more than six hours before the coordinated release — Chainguard published built images containing the fix [3]. Whatever one thinks of those decisions, they settle an argument about windows: once a patch exists anywhere, the clock is public. A defender who needs a week to roll out an update is not competing with the attacker’s cleverness. They are competing with the calendar.
The industry’s behaviour in those two days shows that it knows this. Cloudflare, Fastly, Google Cloud, Vercel, Akamai, AWS, Imperva, Microsoft and Automattic prepared edge rules and mitigations before the patch landed, explicitly as a bridge for systems that could not update immediately [1]. According to Wikipedia’s account of the disclosure, Wikimedia took its Phabricator instance offline as a precaution, hours before the fix existed [4]. And PHP said the thing vendors rarely say out loud: there is no complete mitigation, and updating is the only safe choice [2].
For anyone running their own server, the practical lessons are less dramatic than the headline and more useful. Know what you are running: an inventory of PHP versions, including the ones inside containers, hosting panels and forgotten subdomains, is the precondition for every deadline you will ever set. Patch to the fixed releases, and treat end-of-life branches as a migration project rather than a patching problem. Shrink the blast radius — one system user and one PHP-FPM pool per site, write permissions only where the application genuinely needs them — and keep database privileges separate, one account per application, never root from a web application. Move secrets out of the document root. Harden the entrance with authentication and rate limits, but do not mistake it for protection of the code: an interpreter bug runs before or beside your login form.
Two honest qualifications belong here, because the usual hardening list oversells itself. Functions like open_basedir and disable_functions raise the cost of an attack, but they are not security boundaries, and once the interpreter’s memory has been corrupted they are not what stands between an attacker and the system. And detection, in this case, does not look like a generic log review. Wiz points at specific signals: unusual multipart requests followed by memory-limit errors or repeated HTTP 500 responses, unexpectedly large or malformed PHPSESSID response cookies, and unexplained PHP worker churn [1]. Those are the traces of a failure happening below the application layer, where application logs contain very little.
None of this makes a system safe. It makes it less exposed to the specific failure that the last two months made visible.
Because the uncomfortable conclusion is not that PHP had a flaw. It is that the flaw was understood, that the delay had structural causes the project itself now names, and that one of the arguments for waiting — “no known path to exploitation” — turned out to be a measurement of the tools available at the time rather than a property of the bug. Somewhere right now, the same argument is being made about another old defect in another widely used piece of software. The tools have changed. That argument no longer buys what it used to.
This article describes one class of vulnerabilities — CVE-2026-109618 and CVE-2026-109619 — as of 23 October 2026. It is not a security guarantee and not a clearance. Further unknown vulnerabilities may exist in PHP, in its extensions, in libraries and in any given application. A patched system remains exposed to configuration errors, permission mistakes and flaws in third-party components. Where in doubt, the original advisories from php.net and the CVE database take precedence.
Sources
- Wiz Research — PHP2Shell: AI uncovers critical RCE vulnerabilities affecting 70% of the Internet
- php.net — PHP security release, 21 October 2026 (8.2.35, 8.3.36, 8.4.27, 8.5.12)
- The PHP Foundation — Today’s PHP security release and how we’re strengthening PHP security
- Wikipedia — PHP2Shell (CVSS vectors, timeline, discoverers)
- NVD — CVE-2026-109618
- NVD — CVE-2026-109619
- OpenAI — Coordinated disclosure of critical vulnerabilities
- Calif — PHP2Shell
