On October 21, 2026, PHP shipped fixes for two critical bugs in the interpreter itself: CVE-2026-109618 and CVE-2026-109619, together nicknamed php2shell. Both are rated 9.2 under CVSS v4.0, need no login, and can end in remote code execution as the web worker user. Because the bugs fire while PHP is still parsing the request, before your code runs, your application's authentication can't stop them. Here is what the bugs are, how to check your own systems, and what to do if you can't patch today.
Simply put: PHP is the helper that reads every visitor's message before your website answers. Someone found a way to write a message so messy that the helper gets confused while cleaning up, and a bad person can use that moment to give it orders. The fix is a new, smarter helper.
The timeline
- October 17: Amazon Threat Intelligence saw an exploitation attempt against its honeypots, four days before the fix.
- October 20: The CVE identifiers leaked online, carrying an incorrect CVSS score of 10.0, and the PHP security team announced a release for the next day.
- October 21: PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35 were released with the advisories. According to the PHP Foundation, the release was moved up a day because of the leak. Amazon, Cloudflare, and watchTowr have since reported attempts in the wild.
The PHP Foundation credited Calif, OpenAI, and Wiz, among others, for the research and responsible disclosure. Calif and Wiz said they used AI models in the work.
CVE-2026-109618: corruption on out-of-memory
Simply put: Imagine putting toys on a shelf and counting each spot before the toy is actually there. If you run out of room halfway, the count says the spot is full but it's empty. Later, when you tidy up, you reach for a toy that isn't real.
When PHP runs past memory_limit, it has historically bailed out of the current operation with longjmp() and jumped straight to request shutdown. That is safe only if nothing half-built is reachable at that point. It wasn't always.
Datadog's analysis of PHP 8.5.11 shows a concrete case: the hash-table insert used while building $_POST bumps the table's counters before it allocates the key. If that allocation is the one that exceeds the limit, the table now counts a bucket that was never initialized. Shutdown then destroys $_POST, walks that bucket as if it were valid, and acts on whatever bytes are there. With careful heap shaping, those bytes can be attacker-controlled.
Getting PHP to run out of memory while the request still looks within limits is easier than it sounds. The advisory lists multipart requests without a usable length, multipart parts with an unbounded number of headers, and empty file controls that don't count toward max_file_uploads but still allocate arrays. In the researchers' lab, the worker returned HTTP 200 and kept running after a successful exploit, so a clean status code proves nothing.
Exposure by SAPI: full RCE was demonstrated on Apache mod_php with default limits. PHP-FPM is susceptible by source analysis, but no complete exploit was shown there. FrankenPHP and LiteSpeed are considered likely susceptible.
The fix removes longjmp() from the out-of-memory path. PHP now raises a "soft" out-of-memory error at the next safe point in the VM. If memory keeps growing past a second, higher threshold, it calls _exit() and the process dies on the spot without touching half-built state.
CVE-2026-109619: stale state across requests
Simply put: A PHP worker serves many visitors, one after another. Sometimes it keeps a sticky note pointing at something from the last visitor that was already thrown away. When the next visitor arrives, the worker reads the old note and grabs garbage, and an attacker can choose what that garbage is.
PHP workers are long-lived. Some worker-wide globals for errors, sessions, objects, and extensions hold pointers into memory that belongs to a single request, and they must be reset before the request heap is freed. A fatal error, including an out-of-memory error, can skip that reset. A warning during late shutdown can also fill the state again after it was cleared. The next request on the same worker then reads a dangling pointer, which is a use-after-free.
Two consumers run early enough to be dangerous:
- INI diagnostics before request startup. Any warning raised while per-request INI values are applied, such as
.htaccessphp_valueonmod_phporPHP_VALUEsent by nginx to FPM. This happens even if the warning is never displayed or logged, so@,display_errors=0, anderror_reportingchanges don't help. - Session upload progress. It's enabled by default and runs before normal session initialization, even if your app never calls
session_start().
The confirmed cases are PHP 8.5's fatal_error_backtraces, which led to RCE on mod_php, and the session upload progress lifecycle, validated on mod_php and FPM. The attacker needs two requests to land on the same worker. Many workers and recycling lower the odds, but they don't remove the bug.
Am I affected?
Simply put: If a website you run uses PHP and you haven't updated it since October 21, assume yes.
Every PHP runtime that serves HTTP is in scope: mod_php, PHP-FPM, php-cgi, FrankenPHP, and LiteSpeed.
| Branch | Fixed in |
|---|---|
| 8.5 | 8.5.12 |
| 8.4 | 8.4.27 |
| 8.3 | 8.3.36 |
| 8.2 | 8.2.35 |
| 7.4 to 8.1 | No release; source backports only |
| 5.4 to 7.3 | End of life; no fix |
Check the runtime that actually serves traffic, not just the CLI on the host. They are often different builds.
# Host or container CLI
php -v
# FPM binary
php-fpm -v
# What the web SAPI really runs: drop this behind auth, hit it once, delete it
<?php echo PHP_VERSION, ' ', PHP_SAPI;Distributions and LTS vendors received the patches in advance and often backport without changing the upstream version number, so a Debian or RHEL package can be fixed while still reporting 8.2.x. In that case trust your vendor's advisory, not php -v.
Patching in practice
Simply put: Get the new PHP, then turn every PHP worker off and on again. Workers that are still running keep the old, broken copy in memory.
- Inventory: hosts, container images, and anything that bundles its own interpreter. Composer dependencies don't matter here; this is the interpreter.
- Update to the fixed release or your vendor's patched package.
- Rebuild images from a fresh base. A cached
FROM php:8.3-fpmlayer still holds the old binary, so pull and rebuild withdocker build --pull. - Restart or replace every worker. A
reloadthat keeps old children alive is not enough. - Verify in production with the version check above.
If you can't patch today
Simply put: These are band-aids. They make the hole smaller, they don't close it.
The PHP advisories are explicit that updating is the only complete fix. Until then:
- For CVE-2026-109619: set
session.upload_progress.enabled=0. On 8.5, also setfatal_error_backtraces=0. Consider leaving upload progress off for good if you don't use it. - For CVE-2026-109618: set
memory_limit=-1administratively so no pool,.htaccess, orini_set()can bring back a finite limit. Pair it with strict request-size limits at the proxy and a container memory limit for availability. Don't useRLIMIT_ASorRLIMIT_DATAinstead, because they make allocations fail, which is exactly the vulnerable path. - At the edge: cap the decoded body size, including requests without a usable
Content-Length. Limit nesting, multipart parts, and headers per part. If the app doesn't accept multipart uploads, block them before they reach PHP. Cloudflare has deployed WAF rules for known vectors, which it calls a stopgap only. - Restart all workers after any INI change.
What to look for
Simply put: Check your logs for the helper suddenly running out of memory before reading any page, and check whether your web server started running commands it never runs.
Hunt from October 17 onwards:
Allowed memory size of ... bytes exhaustedwithUnknown on line 0. The error happened during request startup, before any script ran. Correlate it with oversized POSTs.- Form bodies with enormous single field names, or binary and null bytes in form values. Make sure your WAF can actually inspect bodies that large.
- Worker segfaults, such as Apache's
AH00052 ... Segmentation fault, from failed attempts. - The strongest signal: a web worker spawning a shell, for example
apache2orphp-fpm→sh. Baseline what your app normally executes so this stands out.
Don't rely on status codes or crashes. A successful exploit can return 200 and leave the worker running.
What I take from it
Simply put: Know where all your PHP lives, be able to swap it quickly, and watch for strange behavior, because the next bug won't announce itself either.
These bugs live in the interpreter, not in any framework or app, so application-level defenses never saw them. What makes a fix like this fast is boring groundwork: knowing which runtimes you run and where, images you can rebuild and redeploy on demand, and process-level monitoring that would notice a worker doing something it never does. If you can't answer "which PHP versions are serving traffic right now?" in a few minutes, that's the thing to fix after you patch.
Sources: PHP advisory GHSA-w8hw-w9fx-j79w, PHP advisory GHSA-6wmf-4rv5-p3rp, Datadog Security Labs, Tenable FAQ, CISA alert.
