PHP2Shell explained: how AI made a 2004-era PHP flaw exploitable
Two critical PHP flaws found with GPT-5.6 Sol and Gemini 4 Argon let attackers take over servers. How running out of memory became remote code execution.
In 60 seconds
- On 21 October PHP shipped emergency fixes (8.5.12, 8.4.27, 8.3.36, 8.2.35) for two critical flaws, CVE-2026-109618 and CVE-2026-109619, found by Calif, Wiz and OpenAI with help from GPT-5.6 Sol and Gemini 4 Argon.
- An attacker pushes a PHP worker past its memory limit at exactly the wrong moment, leaving half-built or freed data behind that the same worker later trusts, which can lead to code execution without logging in.
- The bug class had been known since 2004 and judged too hard to exploit; AI-assisted research removed that barrier, and the disclosure leaked, so defenders now have to measure patch times in hours.
On Wednesday the PHP project shipped emergency patches for two flaws that can let a stranger on the internet take over a web server without logging in. PHP runs about 69% of websites whose server-side language is known (W3Techs data cited by Tenable), and the flaws were found by researchers using frontier AI models. The surprising part is that the bug class itself isn't new. The basic idea was first reported in 2004. What AI changed was the cost of proving that an old, dismissed weakness actually works as an attack.
For: EveryoneThe plain-English version
PHP is the programming language behind a huge share of the web: most WordPress blogs, many online shops, forums and company intranets. When you load one of those pages, a small PHP "worker" program on the server handles your request, then moves on to the next visitor's.
Each worker gets a strict memory budget per request. If a request tries to use more than that, PHP stops it immediately. That sounds safe, but "immediately" is the problem.
Picture a hotel housekeeper making up a room. Halfway through, the fire alarm goes off and they run out, leaving the sheets half on and a guest's old toiletries on the sink. In a well-run hotel, someone checks the room before the next guest arrives. In this case, nobody does. The next guest walks in and the hotel's systems log the room as "ready." Anything left in it is now treated as theirs.
That's roughly what the researchers found. An attacker sends a specially built upload request that makes PHP run out of memory at exactly the wrong moment. PHP abandons what it was doing and leaves a half-finished internal structure or a pointer to memory it has already given back. The same worker later trusts that leftover state, either while tidying up or while serving the attacker's next request. By carefully controlling what fills that memory, an attacker can, with effort, make the server run their own commands.
The two flaws have official IDs, CVE-2026-109618 and CVE-2026-109619. Both are rated 9.2 out of 10. Wiz says it showed working attacks against the latest standard container images of WordPress, Drupal and Joomla. The fix is to update PHP to version 8.5.12, 8.4.27, 8.3.36 or 8.2.35. If you have a website, ask your host whether they have done that.
The AI angle: a security firm called Calif found the first bug in August using OpenAI's GPT-5.6 Sol. Google-owned Wiz found the flaws independently using its own AI agent plus Gemini 4 Argon and GPT-5.6 Sol. The models didn't work alone. People who knew PHP's history pointed them at old weak spots. But the AI did much of the slow, fiddly work that used to make these bugs "too hard to bother with."
For: CuriousHow it actually works
Step 1: an old weakness
PHP is written in C. When a request exceeds its memory_limit, PHP doesn't return an error politely up the chain. According to the official advisory, it uses a C function called longjmp() to bail out, jumping straight out of whatever code was running and skipping every cleanup step in between. If that code was halfway through building something, the half-built thing stays where it is.
This has been understood as risky for a long time. Calif's write-up traces it back to 2004, when Stefan Esser found the first memory-limit remote vulnerability in PHP. A 2020 bug report noted that long variables could trigger out-of-memory conditions. In 2024 an external audit by OSTIF and Quarkslab found a related out-of-memory issue, but because nobody proved it could be exploited, it wasn't fixed.
Step 2: running out of memory on purpose
An attacker can't usually send PHP gigabytes of data, because post_max_size caps the request size. So the trick is to send a small request that becomes large once PHP turns it into internal data. The advisory lists three ways to do that, all using multipart uploads, the format browsers use to send files:
| Trick | Why it inflates memory |
|---|---|
| A multipart body with no usable declared length | It can slip past the initial size check and keep feeding data |
| Unlimited headers inside one part | PHP's internal representation is much bigger than the bytes on the wire |
| Empty file fields with nested names | They don't count toward the upload limit, but each one still creates metadata arrays |
The attacker's goal isn't just to run out of memory. They want to run out at a chosen point, while PHP is in the middle of an operation that leaves something dangerous behind.
Step 3: something half-built gets trusted
This is where the two CVEs differ.
- CVE-2026-109618, "RCE via OOM corruption." Some request memory becomes reachable before it is fully initialized. If memory runs out at that moment, the shutdown code later reads or acts on the invalid data. On Apache's mod_php, PHP "unconditionally invokes the request shutdown procedure, even when request startup has failed," the advisory says. Because PHP parses the upload body during startup, this happens before WordPress or any other application code runs.
- CVE-2026-109619, "RCE via stale global state." Long-lived worker processes keep some global variables that point into one request's memory. These must be reset before that memory is freed. A fatal error, including running out of memory, can skip the reset. The next request on the same worker then uses a pointer to memory that now holds the attacker's data. That's a use-after-free.
Step 4: from corrupted memory to commands
Turning "the program read garbage" into "the program runs my code" is the hard, traditional part of exploit writing. You have to shape what lands in the freed memory, and that depends on the PHP version, its build options, extensions and memory layout. This is where Wiz says AI earned its keep: LLMs "can help adapt an exploit to different PHP versions, configurations and memory layouts." Wiz's summary is blunt: "With AI this is no longer a barrier."
Step 5: the fix
The patch removes longjmp() from out-of-memory handling altogether. Hitting memory_limit now raises a delayed error at the next safe point. A second, higher "hard" limit kills the process outright. PHP security team member Alexandre Daubois writes that the change touches 390 files. For a language this old, that is a big structural change, not a one-line fix.
The deep dive
The allocator's error path as attack surface
PHP's request allocator (emalloc and friends) enforces memory_limit on every allocation. On failure it raises a fatal error that unwinds via longjmp() to the request's bailout point. Any C code between the allocation and that point that had published partially initialized state (a hash bucket marked live but not yet filled, a global pointer set before its target is complete) is left in that state. The advisory is explicit that the failing allocation "does not need to be related" to the half-built memory. The attacker only needs some allocation to fail while the vulnerable window is open.
Calif describes a concrete case. A fatal error during a hash-table insert can leave a bucket marked as occupied even though it was never initialized, and later cleanup then consumes it. Since the multipart parser builds nested arrays from attacker-chosen field names, the attacker controls both the memory pressure and roughly which insert is under way when the limit is hit.
Two CVEs, different producers and consumers
The PHP advisory for CVE-2026-109619 frames exploitation as a producer that leaves request-owned state in a globally reachable place, and a consumer in a later request that acts on it before normal initialization. Both run in the same persistent worker.
| CVE-2026-109618 | CVE-2026-109619 | |
|---|---|---|
| Official name | RCE via OOM corruption | RCE via stale global state |
| Root cause | longjmp() OOM exit while request memory is reachable but uninitialized |
Global state pointing into a freed request heap, left over when a fatal error skips its reset |
| Confirmed path | mod_php: OOM during startup body parsing, then shutdown walks invalid state | (a) PHP 8.5 fatal_error_backtraces with stale backtrace read by an early INI diagnostic (mod_php); (b) session upload-progress decoder state after an OOM (mod_php and FPM) |
| PHP-FPM | Source analysis says susceptible; "no complete FPM exploit has been demonstrated" | Upload-progress path validated on FPM |
| Main stopgap | memory_limit=-1, enforced so apps can't override it |
session.upload_progress.enabled=0; on 8.5 fatal_error_backtraces=0 |
Sources: GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp. One caveat: Wiz's write-up describes four demonstrated bugs grouped under the two CVEs, without mapping them the same way. We follow the PHP security team's advisories, and Daubois specifically warns that the most repeated advice, disabling upload progress, addresses 109619, not 109618.
A subtle detail in 109619: the early INI-diagnostic consumer doesn't need the warning to be displayed or logged. Turning off display_errors, setting log_errors=Off or using @ doesn't help, because the code path that formats the diagnostic already touches the stale error state.
Why the server architecture decides everything
Both bugs need persistence: state from request must survive into request on the same process. That's why the process model matters so much.
- mod_php (prefork) reuses workers and runs shutdown even after startup failed, which is the worst case for both bugs.
- PHP-FPM exits the child when startup fails, closing the startup-time path for 109618. Exposure comes back when the out-of-memory error hits during script execution, inside a reachable decode such as
session_start()orunserialize(), as Calif describes. That makes FPM exploitation depend on the application. - FrankenPHP and LSAPI are rated likely or conditional by source analysis but not validated.
Multiple workers add noise, because the attacker's follow-up request has to land on the worker they corrupted. The advisory says additional workers and unrelated traffic "can increase the required number of attempts but do not remove the condition." As an illustration (not a figure from the sources): if each attempt lands on the right worker with probability , the chance of at least one success in attempts is
which approaches 1 quickly for an attacker who can send thousands of cheap requests. This is why the CVSS v4.0 vector is AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H. The "high complexity" and "attack requirements present" flags bring the score to 9.2 rather than 10, but they don't make the attack impractical.
The fix: making OOM a soft event
The patch replaces the immediate unwind with two thresholds:
- Soft OOM: exceeding
memory_limitrecords a pending error, which is raised at the next VM safe point, where state is consistent. - Hard OOM: a new
memory_hard_limit_percentsetting (default 200% ofmemory_limitfor limits below 1 GB, per Daubois) ends the process with_exit()if memory keeps growing.
The advisory's promise: "The shutdown procedure will no longer see invalid state." 109619 is fixed separately with a complete state-reset patch. Daubois draws a useful comparison with other runtimes. Go treats out-of-memory as a fatal throw that recover() can't catch, and Rust's default handle_alloc_error aborts. Both refuse to unwind through code that isn't prepared for it. C++ shares PHP's old risk, because std::bad_alloc can be thrown into code that doesn't expect it.
The advisory also contains a warning worth noticing: don't use RLIMIT_AS or RLIMIT_DATA as a defence, because making mmap() fail triggers exactly the vulnerable path. Even memory_limit=-1 is incomplete for the same reason. Lifting PHP's own cap doesn't stop the operating system from refusing an allocation.
What the AI actually did
The accounts differ in emphasis, and the differences matter:
| Group | Models/tools | Stated role | Timeline |
|---|---|---|---|
| Calif (OpenAI "Patch the Planet") | GPT-5.6 Sol (per OpenAI and press) | Variant hunting from PHP's bug history; "no special harness or restricted model access" | Found Aug 1, reported Aug 11, PoC Aug 14, second bug Aug 21 (Calif) |
| Wiz Research | Wiz Atlas, Gemini 4 Argon, GPT-5.6 Sol | Discovery, exploit development, cross-version adaptation, detection, patch validation | Found Aug 19, reported Aug 24 (Wiz) |
| PHP security team | Claude Mythos (via an Alpha-Omega scan), OpenAI Daybreak Red | Validating reports and grouping duplicates; "a person makes every call and works on every patch" | — (PHP Foundation) |
Calif's own caution is worth quoting: "No model is simply capable of unearthing 'all bugs' with simple prompts. At least for now." It also notes that LLMs tend to focus by default on newer, high-profile bug types. The human contribution was knowing that an exploit-unproven issue from 2024 was worth revisiting. Two teams independently reaching the same bug class within about three weeks suggests the discovery was "in the air" once capable models were pointed at it, which matters for the next section.
Disclosure under stress
The coordinated release was planned for 22 October. Then the CVE IDs started circulating on forums with a wrong CVSS of 10.0, Calif "mistakenly published a video" of a proof of concept, and Chainguard shipped patched container images more than six hours before the embargo lifted, which let "any attacker… study the fix in binary form," according to the PHP Foundation. PHP moved the release forward to 14:00 UTC on 21 October. Cloudflare had WAF rules live from 03:02 UTC that day, and it observed attackers mutating payloads to get around them. Amazon's honeypots recorded a first exploitation attempt on 17 October, from an IP address associated with a commercial anonymization network.
For: EveryoneWhy it matters
For everyday users: you can't patch this yourself. Your bank, your favourite forum and your local council's website have to. Expect some disruption. According to The Next Web, Pinboard, the GrapheneOS forum and OBS pulled parts of their sites offline, Laravel paused Cloud deployments, and CERT-EU and Britain's NCSC advised taking systems offline if they couldn't be patched right away. If a service you use announces an incident in the coming weeks, it may trace back to this window.
For developers and site operators: check every place PHP hides, not just your main web stack. That includes old Docker images, vendor appliances, control panels and staging servers. Security researcher Silas Cutler roughly estimates, as cited by Tenable, that more than 1.3 million internet-exposed hosts are potentially vulnerable. Amazon advises starting incident investigations from 17 October, not from disclosure day. Even after patching, PHP recommends leaving session.upload_progress.enabled off if you don't use it, and Daubois suggests capping multipart parts and headers at the frontend, because a patched PHP still kills workers that run out of memory.
For companies: AWS's advice is the headline for any security team: for critical internet-facing flaws, "measure patch and mitigation timelines in hours", not the 7- or 30-day windows written into many policies. Its other line generalizes too: "An embargo protects defenders only until someone else finds the same bug." Two independent teams finding this flaw within weeks suggests that assumption now needs to be built into planning.
For the field: the most important change is economic. Security teams have long triaged bugs by "is this practically exploitable?" A memory bug that needs painstaking, layout-specific exploit work could sit in a backlog for years, as the 2024 audit finding did. If models make that work cheap, the backlog of "theoretical" bugs in old C codebases becomes a stockpile someone will eventually use. OpenAI says it expects "similar findings to increase sharply", and the PHP Foundation reports 15 CVEs since 1 July, plus more than 100 invalid, duplicate or non-reproducible reports. That load falls on what the Foundation calls "a small group of foundation-funded developers." The bottleneck is shifting from finding bugs to fixing them.
For: CriticalWhat to be skeptical of
"AI found it" is doing a lot of work. The two discoverers tell different stories. Wiz credits Gemini 4 Argon with an "invaluable contribution," and OpenAI frames the episode as a "Defender's Window." Calif, the first finder, stresses that generally available models and no special tooling were enough, and that human knowledge of PHP's past bugs was the key. Nobody has published prompts, token counts or failed attempts, so the size of the AI's marginal contribution can't be measured from outside. Each of these companies also has a commercial interest: OpenAI and Google sell the models, Wiz sells security products, and Calif worked within OpenAI's Patch the Planet programme (The Next Web reports that OpenAI funded the audit).
Exploitability is real but uneven. Full code execution was validated against mod_php under default limits. For PHP-FPM, the most common modern setup, 109618 has "no complete FPM exploit," and 109619's FPM path depends on the application. "70% of the web is exposed" (Wiz's framing) blurs that distinction. Exploit reliability also depends on build, extensions and allocator layout, per the advisory.
Exploitation claims vary. Amazon calls PHP2Shell "broadly exploited." The PHP team found no working public exploit at release. Cloudflare reports attack traffic but gives no count of blocked requests or compromised customers. An "exploitation attempt" on a honeypot isn't the same as a successful break-in, and Amazon itself says it's unclear whether the attempts it saw on 21 October would have succeeded against real-world systems.
Version claims were inflated. Early reports said every version since PHP 5, including a PHP 6 that never shipped. Daubois notes the mechanism goes back to PHP 5.3's allocator but that no one has shown an exploit on those old versions. Treat claims about very old versions as plausible but unproven.
The disclosure process itself failed in places, from the leaked CVSS score to the accidental video and the early container images. A faster, AI-driven discovery pipeline doesn't help much if the human coordination around it leaks.
For: EveryoneWhat to watch next
- CISA's Known Exploited Vulnerabilities catalog. If either CVE is added, that would officially confirm in-the-wild exploitation and set patch deadlines for US federal agencies.
- A working PHP-FPM exploit for CVE-2026-109618. If one is published, the exposed population grows sharply.
- Breach disclosures dated back to 17 October, especially from hosting providers and companies running unsupported PHP 7.x.
- Follow-up variants. Wiz says "there are other tracked, and likely untracked, variants." The new soft/hard OOM design will attract scrutiny of its own.
- More "Patch the Planet"-style disclosures. OpenAI expects a sharp increase. Compare with earlier AI-driven offence we've covered: XBOW's six-day Chrome exploit, also built with GPT-5.6 Sol, and Cairn's AI-agent intrusions. For background on the Google model involved, see Gemini 4 Argon explained.
- PHP Foundation funding. Watch whether hosting companies and governments answer its call for sponsorship of security work.
Check your understanding
Pick an answer — you'll see why right away.
1. Why did a related out-of-memory issue found in a 2024 audit go unfixed, while the 2026 version became a 9.2 critical?
2. Why is Apache mod_php the highest-risk setup for CVE-2026-109618?
3. A site owner can't patch today and sets session.upload_progress.enabled=0. What have they achieved?
4. What is the most accurate summary of the AI's role, based on the discoverers' own accounts?
Glossary
- Remote code execution (RCE)
- When an attacker on the internet can make a server run commands of their choosing.
- Use-after-free
- A memory bug where a program keeps using a piece of memory after giving it back, so whatever moved in there next gets treated as the old data.
- memory_limit
- The PHP setting that caps how much memory a single request may use (often 128 MB by default).
- longjmp()
- A C function that jumps straight out of deeply nested code to an earlier point, skipping every cleanup step in between.
- mod_php / PHP-FPM
- Two common ways of running PHP behind a web server: mod_php embeds PHP inside Apache's worker processes, while PHP-FPM runs a separate pool of PHP worker processes.
- Multipart request
- The HTTP format browsers use to upload files and forms, with the body split into separate parts that each have their own headers.
- Embargo
- An agreed period during which a vulnerability is kept secret so a fix can be prepared before attackers learn about it.
- WAF (web application firewall)
- A filter in front of a website that blocks requests matching known attack patterns.
- CVSS
- A 0-10 scale used to rate how severe a vulnerability is; 9.0 and above counts as critical.
Questions people ask
What is PHP2Shell?
PHP2Shell is the nickname for two critical PHP vulnerabilities, CVE-2026-109618 and CVE-2026-109619, disclosed on 21 October 2026. Both can let an unauthenticated attacker run code on a server running a vulnerable PHP version. PHP rates both 9.2 out of 10 on CVSS v4.0.
Which PHP versions fix PHP2Shell?
PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. For the unsupported 7.4 to 8.1 branches, PHP published minimal source patches but no official releases. Older end-of-life versions get no fix from PHP, so upgrade or ask your Linux distribution or hosting provider.
Is my WordPress site affected by PHP2Shell?
Possibly, because the flaw is in PHP itself, not in WordPress. Wiz says it demonstrated unauthenticated code execution against the latest WordPress, Drupal and Joomla container images. Ask your host which PHP version your site runs and make sure it is a patched release.
Is PHP2Shell being exploited?
Yes, exploitation attempts are under way. Amazon says it saw a first attempt on its honeypots on 17 October, four days before disclosure, and now describes PHP2Shell as being broadly exploited. Cloudflare and watchTowr also report attack traffic. PHP's own team said it found no working public exploit at the moment of release.
Did AI find the PHP vulnerabilities?
AI helped. Calif found the first bug in August using OpenAI's GPT-5.6 Sol (according to OpenAI) as part of OpenAI's Patch the Planet initiative, and Wiz independently found the flaws using its Wiz Atlas agent, Gemini 4 Argon and GPT-5.6 Sol. In both cases human researchers directed the work, and Calif says knowing PHP's bug history mattered more than the model.
Can I mitigate PHP2Shell without updating?
Only partially. PHP lists stopgaps such as setting memory_limit=-1, disabling session.upload_progress.enabled, setting fatal_error_backtraces=0 on PHP 8.5 and blocking multipart uploads you don't need, but it says updating is the only complete fix. Cloudflare's WAF rules help only for traffic that passes through Cloudflare.
Discussion
- Loading comments…
Sources
- RCE via OOM corruption (GHSA-w8hw-w9fx-j79w, CVE-2026-109618) — PHP security team (php-src on GitHub) · official announcement
- RCE via stale global state (GHSA-6wmf-4rv5-p3rp, CVE-2026-109619) — PHP security team (php-src on GitHub) · official announcement
- Today's PHP security release and how we're strengthening PHP security — The PHP Foundation · official announcement
- php2shell — Calif · analysis
- PHP2Shell: AI uncovers critical RCE vulnerabilities affecting 70% of the Internet — Wiz Research · analysis
- Patch the Planet: global response coordination as agents uncover critical vulnerabilities — OpenAI · official announcement
- Amazon Threat Intelligence identifies PHP2Shell exploitation prior to public disclosure in MadPot — AWS Security Blog · analysis
- What the PHP2Shell rumors got wrong, from inside the PHP security team — Alexandre Daubois · analysis
- Cloudflare deploys protections against php2shell vulnerability — Cloudflare · analysis
- php2shell (CVE-2026-109618, CVE-2026-109619): Frequently asked questions about critical PHP remote code execution — Tenable · analysis
- PHP rushes out emergency patches for two critical bugs AI found first — The Next Web · news
- PHP Armageddon: OpenAI, Google's Wiz find ancient holes, panicking the internet — TechCrunch · news
How this was made: researched and written by an AI model (Claude) from the primary sources listed above, then checked claim-by-claim against those sources in a separate AI fact-check pass. Spotted an error? Email [email protected] and we correct it publicly. Our process.