PHP2Shell: CVE-2026-109618 and CVE-2026-109619 Explained, With Fixed Versions and the First-Hour Runbook

CVE-2026-109618 and CVE-2026-109619, disclosed by the PHP project on October 21, 2026, are two critical severity (CVSS v4.0 9.2) memory-safety vulnerabilities in the PHP engine that allow an unauthenticated attacker to execute code on any server running PHP behind a web server. Researchers have named the pair PHP2Shell (also written php2shell and PHP 2 Shell). Fixes shipped the same day in PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35.
This post is for anyone running PHP in production in October 2026. Below: the affected and fixed versions, how an out-of-memory error becomes remote code execution, the settings PHP published for teams that cannot patch today, what to hunt for in logs, and why a bug first spotted in 2024 shipped a fix two years later.
What PHP2Shell is: two critical RCE bugs in PHP's memory and error handling
PHP2Shell covers CVE-2026-109618 (PHP calls it "RCE via OOM corruption") and CVE-2026-109619 ("RCE via stale global state"). Both let an unauthenticated attacker run code with the web worker's privileges, and both sit in code paths nearly every PHP request touches: memory management and error handling.
The PHP project itself does not use the name PHP2Shell. The name comes from the research community (Sansec, HeroDevs, Tenable, and others adopted it within hours of disclosure) and is what practitioners will search for. The PHP Foundation's disclosure post and the two GitHub advisories (GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp) are the primary sources.
| CVE | PHP's title | Root cause class | CVSS v4.0 | Confirmed RCE targets |
|---|---|---|---|---|
CVE-2026-109618 | RCE via OOM corruption | Partially initialized request state used after a memory_limit bailout (CWE-416) | 9.2 Critical | Apache mod_php with default settings; PHP-FPM through application-stage paths |
CVE-2026-109619 | RCE via stale global state | Use-after-free of state retained across requests in a reused worker | 9.2 Critical | mod_php via PHP 8.5 fatal_error_backtraces; mod_php and PHP-FPM via session upload progress |
Every SAPI that serves web requests is in scope: PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, and LiteSpeed. PHP rates mod_php as the highest risk. Sansec notes the vulnerable code dates to PHP 5.4, released in 2012, so the exposure window spans 14 years of releases.
| Branch | Vulnerable | Fixed |
|---|---|---|
| PHP 8.5 | Before 8.5.12 | 8.5.12 |
| PHP 8.4 | Before 8.4.27 | 8.4.27 |
| PHP 8.3 | Before 8.3.36 | 8.3.36 |
| PHP 8.2 | Before 8.2.35 | 8.2.35 (branch reaches end of life December 31, 2026) |
| PHP 7.4, 8.0, 8.1 | End of life | Limited source backports only, on branches PHP-7.4-CVE-2026-109618-109619, PHP-8.0-CVE-2026-109618-109619, PHP-8.1-CVE-2026-109618-109619 |
How the PHP2Shell attack chain executes
Both bugs turn a fatal error into a memory-safety failure. CVE-2026-109618 corrupts memory when a request crosses memory_limit in the middle of setup. CVE-2026-109619 lets a later request on the same worker read memory that a previous request freed.
CVE-2026-109618: out-of-memory corruption during request startup
PHP marks certain request state as ready before all of its allocations finish. A crafted multipart body pushes the request past memory_limit during startup. HeroDevs describes the lever: a single multipart part can carry an unlimited number of headers, and PHP's in-memory representation of those headers costs far more than they take on the wire. The allocation fails, PHP bails out, and the half-initialized state is never rolled back.
Under Apache mod_php, request shutdown still runs after a failed startup. Shutdown processes the broken state before a single line of application code executes, which is why PHP's advisory confirms unauthenticated RCE over HTTP against mod_php with default input and memory limits, including multi-worker prefork deployments. Calif, one of the reporting firms, also triggered the bug with an ordinary application/x-www-form-urlencoded post on mod_php, so blocking multipart alone is not a complete answer there.
PHP-FPM behaves differently. FPM kills the worker when startup fails, which removes the simplest path. The FPM route needs an application-stage allocation after startup succeeds, for example parsing a second request body during script execution. Red Hat's statement calls FPM exploitation "difficult in practice" for this reason, and PHP's advisory reports no complete FPM exploit for this CVE. Source analysis still supports FPM susceptibility.
CVE-2026-109619: stale state that survives across requests
PHP reuses a worker process across many HTTP requests. A fatal error in one request (memory exhaustion, an execution timeout) interrupts cleanup and leaves internal pointers aimed at memory that has been freed. The next request on that worker consumes the stale value before normal initialization can clear the pointer.
Two consumers are confirmed. The first is PHP 8.5's fatal_error_backtraces feature, enabled by default, where a later initialization diagnostic reads stale backtrace state; PHP confirms RCE against mod_php through this path. The second is session upload progress, also enabled by default and reachable without any call to session_start(); PHP confirms RCE against both mod_php and PHP-FPM through the session lifecycle. On FPM, both the poisoning request and the consuming request must land on the same worker. More workers and faster recycling lower the odds per request but do not remove the condition.
Impact
Successful exploitation yields code execution as the web worker user. Sansec also reports that the upload-progress path can leak freed worker memory back to the client inside a PHPSESSID response cookie, a Heartbleed-style disclosure that needs no code execution at all. Researchers confirmed unauthenticated RCE against Magento 2.4.9 and ten other PHP applications. Rapid7 and Red Hat both note that reliable exploitation depends on precise heap manipulation and timing, so exploit portability varies by PHP build, loaded extensions, SAPI, and memory layout.
The two-year timeline: why a bug found in 2024 shipped a fix in October 2026
The PHP Foundation says the first signs of the OOM bug surfaced in 2024, while a contracted core developer was fixing a finding from PHP's first external audit (run through OSTIF, performed by Quarkslab, funded by Germany's Sovereign Tech Agency). An exploit was theorized. No concrete path to code execution was found, and the issue was outranked by findings with clear impact. The Foundation calls the delay that followed a structural failure.
The root cause of the delay was organizational, not technical ignorance. The security team developed several competing fixes, each with a cost: backward-compatibility breaks, new denial-of-service potential, or coordinated changes across a large ecosystem of third-party extensions. The team operated by consensus, feared shipping the wrong trade-off, and had a handful of engineers with deep engine knowledge already stretched across other work. Work on a fix stalled and restarted for months. The Foundation has since moved the security team toward majority decisions and stated that a stuck fix would now reach Foundation leadership much sooner.
| Date (UTC) | Event |
|---|---|
| 2024 | External audit (OSTIF, Quarkslab). A core developer finds early signs of the OOM problem; an exploit is theorized, not demonstrated |
| May 2026 | PHP Foundation creates the Ecosystem Security Team |
| July 30, 2026 | Calif begins an audit of PHP under OpenAI's Patch the Planet program |
| August 11, 2026 | Calif (researcher Ryan A) reports unconditional RCE with the same root cause; OpenAI independently reports the same root cause the same day. Fixing it becomes the top priority for the Foundation's security staff |
| August 24, 2026 | Wiz reports further attack vectors |
| September 18, 2026 | OpenAI reports additional vectors |
| October 20, ~15:00 | Both CVE IDs leak on forums and social media with a wrong CVSS of 10.0 (actual 9.2). PHP publishes a pre-announcement at 19:30 and moves the release from October 22 to October 21 |
| October 20, ~20:00 | Calif mistakenly publishes a proof-of-concept video showing exploitation against several targets; the video is deleted, copies circulate |
| October 21, 07:41 | Chainguard breaks the embargo and publishes patched images more than six hours before the coordinated release |
| October 21, 14:00 | Coordinated release of 8.5.12, 8.4.27, 8.3.36, 8.2.35 |
| October 21, 14:19 | watchTowr reports active exploitation attempts against both CVEs |
What makes this fix notable is its depth. Crossing memory_limit now raises a delayed soft OOM at the next safe point in the virtual machine, with a hard _exit() if memory use hits a second, higher ceiling. That is a change to PHP's memory model across four branches, which is why the patch needed a funded, full-time team and why distributors needed lead time to package the release.
The Foundation also credits tooling for the volume of reports it now handles: a scan of PHP with Anthropic's Claude Mythos through Alpha-Omega, OpenAI's Daybreak Red for validating reports and exploits, plus sanitizer builds and reproduction environments. Since July 1 the team has published 15 CVEs and worked through more than 100 reports that were invalid, duplicates, or not reproducible. A person still makes every call and writes every patch.
Indicators of compromise and behavioral signals for PHP2Shell
There are no file hashes, C2 domains, or malware samples to block. PHP2Shell is an engine bug, so the signals are request shapes, memory behavior, and crash patterns. Hunt for the conditions an attacker must create, not for a payload.
Request indicators
- Multipart bodies with abnormal header counts per part, deep nesting, or many parts arriving under a small
Content-Length. - Form posts with field names of roughly 1,000 bytes or more. Abstract Security reports every variant they analyzed needs a key large enough that copying it exhausts
memory_limitduring request startup. - Requests with no usable
Content-Lengthreaching PHP endpoints that never accept uploads.
Log and crash indicators
- PHP fatal errors reading
in Unknown on line 0in web server error logs. - Clusters of
Allowed memory size exhaustedfatals from a single source, especially before application code runs. - Worker segfaults or unexpected PHP-FPM child exits paired with either of the above.
PHPSESSIDresponse cookies containing unexpected binary or structured data (the memory-disclosure path).
Runtime and process indicators
- PHP worker processes spawning shells or unexpected child processes.
- Outbound connections from PHP workers that the application never makes.
- New files written to web roots or temp directories by the worker user.
Configuration state to inventory
- PHP version and SAPI per host. Rapid7 published a Velociraptor artifact,
Generic.Applications.PHPInventory, that collects package versions, binary and module paths, and running PHP processes across Windows, Linux, and macOS. - Whether
memory_limit,session.upload_progress.enabled, andfatal_error_backtracesare set administratively or left to application defaults.
PHP states plainly that the absence of relevant log entries is not proof an installation is safe, and that hiding diagnostics through error_reporting, display_errors, log_errors, or the @ operator does not prevent stale-state access.
Immediate response: the first-hour runbook
Patch first; everything else is a stopgap. PHP states that an update containing both complete fixes is the only full remediation.
- Inventory every PHP host, version, and SAPI. Prioritize Apache mod_php with default settings, then anything internet-facing on PHP-FPM, FrankenPHP, LiteSpeed, or php-cgi.
- Update to
8.5.12,8.4.27,8.3.36, or8.2.35, or to the distribution package that carries both fixes. On end-of-life branches, build from thePHP-7.4,PHP-8.0, orPHP-8.1CVE backport branches or get a vendor build. Users ofrpms.remirepo.nethad no update on October 21; PHP advises switching to distribution packages or building from source. - Restart every process that runs PHP. PHP-FPM pools, Apache, php-cgi, FrankenPHP, LiteSpeed, Laravel Octane, and queue workers. A patched binary with an old worker still resident is unpatched.
- If you cannot patch within hours, apply PHP's temporary settings administratively. Use
php_admin_valuein FPM pools or Apache configuration so applications cannot override them.ini_set()and.user.inirun too late to help.Setting Covers Side effect memory_limit = -1CVE-2026-109618confirmed exploit pathOne request can consume all RAM. Add a container or cgroup memory limit as a safety net and isolate workers so the master process is not killed with them session.upload_progress.enabled = 0CVE-2026-109619session path on mod_php and FPMUpload progress bars stop working fatal_error_backtraces = 0(PHP 8.5 only)CVE-2026-109619backtrace path on mod_phpFatal errors lose their backtraces post_max_size = 8M(32Mwhere uploads are needed),upload_max_filesizebelow itReduces pressure on CVE-2026-109618Large forms and uploads may fail - Bound request shape at the proxy or web server. Limit decoded request size, multipart part counts, headers per part, and nesting depth, including requests without a usable
Content-Length. Block multipart entirely on endpoints that never accept uploads. Do not substituteRLIMIT_ASorRLIMIT_DATA; those can trigger the vulnerable allocation-failure path. - Hunt using the log and runtime indicators above. Treat missing log entries as inconclusive.
- Rotate credentials reachable from any worker that shows signs of compromise, and freeze production changes until the fleet is confirmed on fixed versions.
- Restore a real
memory_limitonce patched. The fix adds a new hard ceiling, and reaching it ends the process and skips shutdown handlers, so checkmemory_limitandpost_max_sizeagainst what the application needs.
Why conventional tooling sees the version and misses the exploit
Vulnerability scanners and SCA will flag the PHP version within a day. That tells you where to patch, not whether someone got in. Detecting PHP2Shell in motion requires watching what the PHP worker does after the request, not what the request contains.
This is not a vulnerable dependency. This is the interpreter. Signature matching has nothing to match until a public exploit stabilizes, and the exploit itself is a memory-layout exercise that varies by build, extensions, and SAPI. A WAF rule on multipart shape will catch the crude proofs of concept circulating since October 20 and miss the tuned ones. Calif's form-urlencoded trigger on mod_php shows how quickly a request-shape rule can be sidestepped.
The gap is structural, and it is the same gap every engine-level bug exposes: static inventory answers "is the vulnerable version present," runtime answers "is it running, is it reachable, and did it just do something a PHP worker never does." Runtime intelligence tells you which PHP workloads are actually serving attacker-reachable entry points, so the patch queue is ordered by exposure rather than by asset count. Application Detection and Response watches worker behavior (process spawns, unexpected outbound connections, file writes to the web root) and detects the post-exploitation step regardless of which memory trick got the attacker there, without a signature for the exploit itself. Kai folds the version data, the runtime exposure, and the detection context into one ranked list for the engineer on call. The distinction between a package that is present and one that is being exploited is the whole argument of reachability to reality, and PHP2Shell is a clean example of that argument.
Hardening PHP deployments against the next PHP2Shell variant
PHP's own advisory expects additional vectors, and the research community is actively reproducing this work. Assume more stale-state consumers will be found. Harden now so the next advisory is a routine patch instead of an emergency.
- Move off Apache mod_php to PHP-FPM where feasible. FPM's worker-exit-on-startup-failure behavior removed the easiest path for
CVE-2026-109618. - Set
memory_limit,session.upload_progress.enabled, andfatal_error_backtracesadministratively so applications cannot override them, and codify that in configuration management. - Enforce request-shape limits at the edge as a standing control. Multipart part counts, headers per part, nesting, decoded size. This belongs in the baseline, not in the incident response.
- Recycle FPM workers aggressively with
pm.max_requeststo shorten the window for cross-request stale state. - Retire PHP 7.4, 8.0, and 8.1 now. PHP 8.2 joins them on December 31, 2026. The backports for this incident were a courtesy, not a commitment.
- Fix attacker-reachable fatal errors and timeouts in application code. PHP lists them as the trigger for stale-state exposure, and PHP's guidance to correct invalid or deprecated per-request settings applies here too.
- Add behavioral detection on PHP workers so the next memory bug surfaces as an anomalous process tree, not as a breach notification. Runtime application protection is the control that does not depend on knowing the exploit in advance.
What PHP2Shell signals about interpreter-level risk
PHP2Shell is the first widely covered case where AI-assisted auditing turned a two-year-old theoretical bug into confirmed unauthenticated RCE within weeks, and the same tooling is available to attackers. Expect more engine-level findings, shorter disclosure windows, and messier embargoes.
- Report volume is outpacing maintainer capacity. Fifteen CVEs and more than 100 invalid reports in one quarter, handled by what the Foundation describes as a small group of funded developers plus volunteers. Tools find bugs; experienced engineers still have to decide on the fix and weigh the compatibility cost.
- Coordinated disclosure is fragile. A leak, a mistaken proof-of-concept video, and a unilateral early publish by a distributor all happened inside 24 hours, each one shrinking the window defenders had.
- Patching the interpreter is change management, not a package bump. The fix altered PHP's memory model across four branches. Fleets that treat PHP as a base image detail, not a tracked asset, will be the last to finish.
One prediction to hold us to: a reliable public exploit for the mod_php path of CVE-2026-109618 lands in a mainstream framework (Metasploit or equivalent) within 30 days of October 21, 2026, and CISA adds at least one of the two CVEs to the Known Exploited Vulnerabilities catalog in the same window.
Frequently Asked Questions
PHP2Shell is the name researchers gave to two critical PHP vulnerabilities, CVE-2026-109618 and CVE-2026-109619, disclosed October 21, 2026. Both allow unauthenticated remote code execution against PHP web workers. The PHP project itself does not use the name.
Every PHP release before 8.5.12, 8.4.27, 8.3.36, and 8.2.35 is vulnerable, and the underlying code dates to PHP 5.4. PHP 7.4, 8.0, and 8.1 get limited source backports only.
Somewhat. FPM kills a worker when request startup fails, which blocks the simplest CVE-2026-109618 path. FPM is still confirmed exploitable through the CVE-2026-109619 session upload progress path and through application-stage out-of-memory conditions.
Set memory_limit = -1, session.upload_progress.enabled = 0, and on PHP 8.5 fatal_error_backtraces = 0, all administratively via php_admin_value, then restart every worker. PHP states these reduce exposure on specific paths and are not a complete fix.
Look for in Unknown on line 0 fatals, clustered memory-exhausted errors, worker crashes, and PHP processes spawning shells or making unexpected outbound connections. Absence of log entries does not prove you are clean.
watchTowr reported active exploitation attempts against both CVEs at 14:19 UTC on October 21, 2026. Neither CVE was in the CISA KEV catalog at disclosure; check the catalog for current status.
SCA and vulnerability scanners flag the vulnerable PHP version, which is useful for patch scoping. A WAF can block crude multipart shapes. Neither sees the memory corruption itself; behavioral detection on the worker is what catches the post-exploitation step.
The PHP Foundation says the OOM problem was spotted in 2024 without a known RCE path, and competing fixes carried compatibility and performance trade-offs that a consensus-driven security team could not resolve. Funded full-time work after the August 2026 reports landed the fix in ten weeks.
The bottom line
The next critical bug may sit below your dependency tree, in the interpreter itself, where no manifest lists it and no signature exists on day one. Kodem ADR watches what your PHP workers do at runtime, so an exploit that nothing matches still trips a behavioral detection, and Kai orders the patch queue by real exposure. See Kodem ADR in action.
References
- The PHP Foundation: Today's PHP Security Release, and How We're Strengthening PHP Security
- PHP advisory GHSA-w8hw-w9fx-j79w (CVE-2026-109618, RCE via OOM corruption)
- PHP advisory GHSA-6wmf-4rv5-p3rp (CVE-2026-109619, RCE via stale global state)
- Rapid7 Emergent Threat Response: CVE-2026-109618 and CVE-2026-109619
- The Hacker News: PHP Issues Emergency Patches for Two Critical PHP2Shell RCE Flaws
Related blogs

LiteLLM Supply Chain Attack: The 1.107.0 Backdoor That Runs Without an Import
The LiteLLM supply chain attack backdoored litellm 1.107.0 on PyPI and Docker. Get the IOCs, what the malware steals, and a first-hour response runbook.
12
Stop the waste.
Protect your environment with Kodem.
The State of the Application Security Workflow
This report aims to equip readers with actionable insights that can help future-proof their security programs. Kodem, the publisher of this report, purpose built a platform that bridges these gaps by unifying shift-left strategies with runtime monitoring and protection.
.avif)
Get real-time insights across the full stack…code, containers, OS, and memory
Watch how Kodem’s runtime security platform detects and blocks attacks before they cause damage. No guesswork. Just precise, automated protection.


