PHP2Shell Is Two Critical PHP Flaws. Most of the PHP on the Internet Can't Take the Patch
php2shell (CVE-2026-109618, CVE-2026-109619) is fixed only in PHP 8.2 and later. Here is how both RCE bugs work and what to do on PHP 8.1 and older.

On October 20, 2026, someone posted to Hacker News that two PHP remote code execution bugs were about to drop, rated critical, affecting everything back to PHP 5. The poster offered no evidence beyond, in their own words, source: trust me, bro.
The same day, the PHP project published an out-of-cycle security notice. By the next afternoon the fixes were out. The leak was real, the score was close, and the two CVEs, CVE-2026-109618 and CVE-2026-109619, are now public with full advisories. Researchers are calling the pair php2shell, and The Hacker News has the full timeline.
Both are rated 9.2 Critical under CVSS 4.0, are unauthenticated, and end in code execution as the web server worker. The advisories list every version before 8.2.35 as affected, which takes in every End-of-Life PHP release line still serving traffic in production.
The leak came before the patch, and the releases only cover four branches
PHP shipped fixes in 8.2.35, 8.3.36, 8.4.27 and 8.5.12 on October 21, 2026, alongside a security advisory covering both CVEs. Those are the four branches PHP officially supports, and 8.2 itself reaches End-of-Life on December 31, 2026. Anything older, which means 8.1, 8.0, 7.4 and everything before them, gets no release. Because the bugs are this severe, the security team did push minimal source backports for 7.4, 8.0 and 8.1 to GitHub, but those are unreleased patches for people who build PHP themselves. A server that gets PHP from packages only sees them if a vendor ships an update.
That older set is not small. On October 20, the day before the release, the threat intelligence company Mallory counted roughly 884,000 internet-facing hosts whose HTTP banner reported PHP 8.1 or earlier on Shodan. That was 59% of the 1.5 million hosts that reported any PHP version at all, and another 740,000 hosts reported PHP with no version string. Banners are not proof of exposure, but they are a decent proxy for how much of the PHP footprint sits on a branch with no official release for this fix.
I wrote earlier this year about how End-of-Life software is the most exposed software there is, because the vulnerability research keeps happening and the patching stops. This week is a clean example. PHP shipped releases for both bugs a day after the leak, and none of those releases goes below 8.2.
CVE-2026-109619 turns a worker's leftover state into a weapon
A PHP worker under mod_php or PHP-FPM serves many requests over its life. Between requests it is supposed to reset the global state it carries: error state, session state, object and extension state. Some of that state points into memory that belongs to a single request, so the reset has to happen before that memory is freed.
CVE-2026-109619 is what happens when it doesn't. A fatal error, a parse failure, an uncaught exception, a failed require, an execution timeout or memory exhaustion can abandon an operation before the reset runs. The request memory is freed. The global still points at it. The next request on the same worker picks it up, and you have a use after free that an attacker can drive.
The attacker needs two things: a producer that leaves state behind, and a consumer that touches it before normal request initialization replaces it. The advisory confirms two consumers. The first is INI diagnostics raised before request init, which means .htaccess and php_value directives under mod_php, or PHP_VALUE and PHP_ADMIN_VALUE parameters sent to FPM, that produce a warning or deprecation. Turning off display_errors, changing error_reporting or using @ does not help. The second is session upload progress, which is on by default, runs without any session_start() call, and can consume session state left behind by an interrupted request.
Remote code execution through the session path was validated against both mod_php and PHP-FPM, though the FPM path needs a suitable operation in the application.
CVE-2026-109618 turns running out of memory into code execution
CVE-2026-109618 is in how PHP bails out when a request crosses memory_limit. It raises a fatal out-of-memory error and uses longjmp() to abandon the current code path. Some code paths make request memory reachable before it is fully initialized. If any allocation fails at that moment, even one unrelated to that memory, the jump skips the rest of initialization and leaves the memory in an invalid state that request shutdown then reads.
An attacker can force the OOM on purpose, and the advisory spells out how. A multipart request without a usable content length can pass the post_max_size check and keep sending body data. A single multipart part can carry an unlimited number of headers, and PHP's internal representation of those headers costs far more memory than they take on the wire. Empty file controls don't count toward max_file_uploads but still allocate metadata, and nested field names multiply the work.
Under mod_php, request shutdown runs even when request startup failed, so automatic body parsing can exhaust memory and shutdown processes the broken state before a single line of application code runs. The advisory confirms unauthenticated RCE over HTTP against mod_php with default input and memory limits. PHP-FPM kills the worker when startup fails, which blocks that specific path, but an OOM later in the request can still leave invalid state for shutdown, and source analysis rates FPM as susceptible, though no complete FPM exploit has been demonstrated.
The fix removes longjmp() from OOM handling entirely. Crossing memory_limit now raises a delayed soft OOM at the next safe point in the VM, with a hard _exit() if memory use hits a second, higher ceiling. That is a deep change to PHP's memory handling, and not something you backport in an afternoon.
The php2shell mitigations are real, but none of them are complete
The PHP project's security advisory is explicit that updating is the only complete fix for either bug. If you cannot update yet, the advisories do offer stopgaps, and some of them are counterintuitive enough to be worth calling out.
For CVE-2026-109618, the primary mitigation is to set memory_limit=-1 administratively, enforce it across every pool and directory, and restart every worker. The confirmed exploit depends on a predictable per-request limit, and removing that limit removes the trigger. It is not a full fix, because an underlying mmap() failure can still take the vulnerable path. Do not reach for RLIMIT_AS or RLIMIT_DATA as a substitute, because those make allocations fail at the limit, which is exactly the condition you are trying to avoid. Keep post_max_size conservative, enforce decoded request size at the frontend, and if your application never accepts multipart form data, block it there.
For CVE-2026-109619, set session.upload_progress.enabled=0 if you don't use it, and the advisory suggests leaving it off even after patching. On 8.5, set fatal_error_backtraces=0. Move INI settings out of per-directory and per-request scope and into php.ini or pool configuration applied at startup. Where performance allows, pm.max_requests=1 limits each worker to one request, which closes the window the first bug depends on.
All of that buys time. None of it replaces the patch.
Here is the problem for everyone on PHP 8.1 and older
Take the roughly 884,000 hosts from Mallory's count. The mitigations above apply to them. The releases do not. There is no PHP 8.1.35, no 8.0.31, no 7.4.34 coming. The advisory says plainly that 8.1 and older "no longer receive updates from the PHP project" and sends everyone else to their vendor. The source backports help if you compile PHP yourself and can carry a patch forward on your own. Most of those hosts never compiled PHP in the first place.
The usual answer is just upgrade. For a lot of these systems that is the right answer, and it has been for years. But the reason they are still on 7.4 or 8.0 is rarely laziness. It is a vendor appliance that bundles its own PHP. It is a control panel that pins a version. It is an application with a dependency tree that breaks on 8.1 and nobody has the budget to rewrite. It is a migration that is scheduled for next year and cannot be pulled forward to this week.
The obvious comparison is CVE-2024-4577, the PHP CGI flaw from June 2024. Akamai saw exploit attempts against its honeypots within 24 hours of disclosure. No exploitation of php2shell has been reported as of October 21, 2026, and no public exploit code is known. That is today. The advisories now describe both bugs in enough detail for a motivated attacker to work from, and the multipart tricks in CVE-2026-109618 are not exotic.
HeroDevs closes the gap for End-of-Life PHP
This is the part where I tell you what we do. I'll keep it short.
HeroDevs provides Never-Ending Support for software after its community support ends. For PHP, both CVE-2026-109618 and CVE-2026-109619 are fixed for End-of-Life PHP.
The PHP project's source backports are a real help if you build PHP yourself and can own the rebuild and testing, then do it all again for the next CVE. NES for PHP is for everyone else. If you are running PHP 7.4, 8.0 or 8.1 in production and can't upgrade this week, it gets you a supported, patched runtime instead of a branch to compile or a list of partial mitigations.
Our vulnerability directory has entries for CVE-2026-109619 and CVE-2026-109618.
Learn more about NES for PHP support through HeroDevs
Resources
View All Articles


