PHP2Shell (CVE-2026-109618 & CVE-2026-109619): What PHP Teams Need to Do Now
Two engine-level PHP bugs turn a single anonymous request into remote code execution

Overview
PHP2Shell is the name given to two critical flaws in the PHP engine itself: CVE-2026-109618 and CVE-2026-109619. Both let an unauthenticated attacker run code on a web server, and the PHP security team scores each 9.2 on CVSS v4.0.
The bugs live in how PHP handles a request, not in your application. On Apache with mod_php, the vulnerable path runs before any of your scripts execute, so login checks, API keys and middleware never get a chance to stop it.
What to do now: upgrade to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 (released October 21, 2026) and restart every PHP worker. PHP 8.1 and older are end of life and stay vulnerable unless your distribution or LTS vendor ships a backport.
| CVE-2026-109618 | CVE-2026-109619 | |
|---|---|---|
| PHP's title | RCE via OOM corruption | RCE via stale global state |
| Root cause | Half-initialized request memory left behind after an out-of-memory error | Per-request state that survives into the next request on the same worker |
| CVSS v4.0 | 9.2 (Critical) | 9.2 (Critical) |
| Authentication needed | None | None |
| RCE confirmed on | Apache mod_php | Apache mod_php and PHP-FPM (session path) |
| Fixed in | 8.2.35, 8.3.36, 8.4.27, 8.5.12 | 8.2.35, 8.3.36, 8.4.27, 8.5.12 |
Timeline: Attackers Moved Before the Patch
Details leaked a day before the fix shipped, and the first known exploitation attempt came four days before public disclosure.
| Date (2026, UTC) | What happened |
|---|---|
| Aug 1 | Researchers at Calif find several RCE paths in PHP |
| Aug 11 | Calif reports the first bug to PHP; coordinated disclosure begins |
| Aug 24 | Wiz Research independently reports the vulnerabilities to PHP |
| Oct 17 | Amazon's MadPot honeypot network records a PHP2Shell exploitation attempt from an anonymized IP |
| Oct 20 | PHP announces an emergency release; details leak; watchTowr says it reproduced CVE-2026-109618 |
| Oct 21, 07:43 | Cloudflare reports early attack traffic |
| Oct 21, 14:00 | PHP ships the fixed releases; Calif, Wiz, OpenAI and PHP publish details |
Wiz researchers noted that the underlying flaws had existed for many years, and that frontier AI models lowered the effort needed to turn them into working exploits. Expect the gap between disclosure and mass exploitation to keep shrinking.
How the Vulnerabilities Work
Both bugs come from the same blind spot: PHP's cleanup code trusting memory that an aborted operation left in a broken state.
CVE-2026-109618: Corruption After an Out-of-Memory Error
When a request hits PHP's memory_limit, PHP jumps straight out of the current code path to abort it. In a few places, request data is already reachable before it is fully built. If memory runs out at exactly that point, the half-built data stays behind, and the request shutdown routine later reads it as if it were valid. With a carefully arranged heap, that becomes code execution.
An attacker can force that out-of-memory condition with a crafted multipart POST while staying inside the usual input limits:
- A multipart body with no usable declared length can pass the first
post_max_sizecheck and keep feeding data. - One multipart part can carry a huge number of headers, which take far more memory inside PHP than on the wire.
- Empty file fields do not count toward
max_file_uploadsbut still create arrays, and nested field names multiply that work.
On mod_php, body parsing happens during request startup and shutdown always runs, so the whole chain completes before any application code. Datadog Security Labs reproduced it on PHP 8.5.11 with one unauthenticated POST against a script that only printed a response. Sansec adds that this bug can also leak freed worker memory back to the client in a session cookie, Heartbleed-style, exposing data such as tokens and keys from other requests.
CVE-2026-109619: Stale State Reused by the Next Request
A PHP worker keeps some global state (errors, sessions, objects, extensions) between requests, and parts of it point into memory that belongs to a single request. If a fatal error cuts that request short before the state is reset, the memory is freed but the pointer survives. When the same worker takes the next request, some steps run before normal initialization and touch that stale pointer: a use-after-free.
Exploitation needs two requests on the same worker, one that leaves the stale state behind and one that consumes it. PHP confirmed two concrete paths:
- PHP 8.5 with
fatal_error_backtracesenabled: backtrace state is never cleared at shutdown; RCE confirmed on mod_php. - Session upload progress: enabled by default and active even if the app never calls
session_start(); RCE validated on both mod_php and PHP-FPM.
More workers and unrelated traffic make each attempt less likely to land, but they do not remove the condition.
Who Is Affected
Treat every PHP installation that serves web requests as exposed until it runs a fixed release. Researchers report the flaws reach back to PHP 5.4.
| PHP branch | Fixed release | Notes |
|---|---|---|
| 8.5 | 8.5.12 | Also disable fatal_error_backtraces if you cannot patch yet |
| 8.4 | 8.4.27 | |
| 8.3 | 8.3.36 | |
| 8.2 | 8.2.35 | |
| 8.1 and older (7.x, 5.x) | None from PHP | End of life; PHP published minimal source backports for 7.4, 8.0 and 8.1, otherwise use a distro or LTS backport |
How PHP is wired into the web server changes the risk:
| Server setup | Risk | Why |
|---|---|---|
| Apache + mod_php | Highest | Both bugs confirmed as unauthenticated RCE; the attack runs before application code |
PHP-FPM (nginx, Apache mod_proxy_fcgi) | High | Session-path RCE for CVE-2026-109619 validated; CVE-2026-109618 judged susceptible from source, no full exploit shown yet |
| FrankenPHP | High | Runs request shutdown like mod_php, so PHP considers it likely open to application-independent attacks |
| LiteSpeed (LSAPI) | Likely | Modeled on FPM; app-specific exploits considered likely but not yet validated |
Because the bugs are in the engine, everything built on PHP inherits them. Calif confirmed unauthenticated RCE against Magento, WordPress, Drupal, Joomla, MediaWiki, Roundcube, phpMyAdmin, Nextcloud, Laravel, Symfony and WHMCS, even where the application code itself was clean.
How to Protect Your Servers
Patching is the only complete fix. PHP's own advisories say so for both CVEs, and every configuration change below only narrows the attack surface.
-
Patch and restart
Install 8.5.12, 8.4.27, 8.3.36, 8.2.35 or your distribution's patched package, then restart every worker: Apache, each FPM pool, every container. Linux distributions and LTS vendors received the fix ahead of release. If you rely on the volunteer-run Remi repository, switch to distribution packages or build from source; it had no update on release day.
-
Handle end-of-life PHP
Get a vendor backport or upgrade to a supported branch. The UK NCSC advises considering taking systems offline if they cannot be fixed right away.
-
Add a WAF layer
Cloudflare deployed rules for the known exploit vectors to all customers, free plans included. Treat this as buying time: attackers were already seen trying to evade those protections.
If You Cannot Patch Today
| Change | Narrows | Limits |
|---|---|---|
Enforce memory_limit=-1 at admin level (e.g. php_admin_value in the FPM pool or Apache config), restart workers | The confirmed CVE-2026-109618 exploit | Not complete: a failed low-level allocation still hits the bug, and one request can now use all memory, so add container memory limits and monitor |
Set session.upload_progress.enabled=0 administratively | The session path of CVE-2026-109619 | Covers that path only; Datadog warns it does not protect on its own. Worth keeping off after patching if you don't use it |
Set fatal_error_backtraces=0 (PHP 8.5) | The confirmed backtrace path of CVE-2026-109619 | Covers that producer only |
Cap decoded body size, multipart parts, headers per part and uploads at the frontend; lower max_input_nesting_level | The out-of-memory tricks behind CVE-2026-109618 | Reduces risk, does not remove it |
Reject multipart/form-data at nginx or Apache where the app does not need uploads | The main CVE-2026-109618 delivery route | Breaks file uploads; test critical flows such as checkout |
Set pm.max_requests=1 in FPM | Worker reuse that CVE-2026-109619 depends on | Heavy performance cost |
Example administrative settings for an FPM pool (for example /etc/php/8.3/fpm/pool.d/www.conf):
php_admin_value[memory_limit] = -1
php_admin_flag[session.upload_progress.enabled] = off
Then restart PHP-FPM so every worker picks up the change.
Two things to avoid: do not use RLIMIT_AS or RLIMIT_DATA as a memory cap, because they make allocations fail in exactly the way that triggers the bug. And do not run proof-of-concept code from random repositories; JFrog flagged fake PoCs that surfaced around the disclosure.
How to Check Whether You Were Hit
A successful attack does not have to crash the worker, so quiet logs are not proof of safety. Review from at least October 17, the date of the first known attempt, and look for:
- Shells spawned by the web server:
apache2,httpdorphp-fpmstartingsh,bashor other unexpected child processes is the strongest signal. - Odd multipart POSTs: very large form submissions, parts with hundreds of headers, or many empty file fields, especially on routes that never take uploads.
- Memory errors during startup: "Allowed memory size exhausted" fatals while the request body is parsed, before your script runs.
- Repeated fatal errors and warnings: right before scripts run, or during worker cleanup.
If you find signs of compromise, treat the host as owned: isolate it, rotate every secret the PHP user could read (database credentials, API keys, .env values, session signing keys), and rebuild from a clean image rather than cleaning in place.
What This Means for API Teams
PHP2Shell shows that a perfectly written endpoint can still be the way in. Most API security work, ours included, targets bugs in your own code: broken object-level authorization, role checks that drift, workflow abuse. PHP2Shell sits one layer lower, in the runtime that parses the request before your API ever sees it. Strong authentication does not help when the attack lands before authentication runs.
Three takeaways for teams that ship APIs:
- Know which of your APIs run on PHP. You cannot patch what you do not know exists. Legacy endpoints, admin panels, internal tools exposed by mistake and side services are the ones that stay unpatched for months.
- Every PHP route that accepts a POST is in scope. Not just upload endpoints: Datadog showed that any PHP script exposes the vulnerable path, even one that only prints a response.
- Keep the layers that limit blast radius. Request size limits at the gateway, rejecting content types an endpoint does not use, and a low-privilege web user mean an RCE does not automatically hand over your database credentials.
ApyGuard does not replace patching here. Where it helps is the first takeaway: an accurate, current map of the APIs you actually expose, so you know where to patch first, and authenticated testing afterwards to confirm your access controls still hold.
Conclusion
PHP2Shell is two critical, unauthenticated RCE bugs in the PHP engine, leaked before the patch and probed in the wild before disclosure. The order of work is simple: patch, restart every worker, apply the temporary hardening only where patching has to wait, then review logs back to October 17.
References
- PHP security advisory: RCE via OOM corruption (CVE-2026-109618)
- PHP security advisory: RCE via stale global state (CVE-2026-109619)
- Datadog Security Labs: PHP2Shell detection, analysis, and remediation
- Sansec: PHP2Shell research and disclosure timeline
- Help Net Security: PHP2Shell patched, update now
- JFrog Security Research: PHP2Shell, all you need to know
After patching your servers, map the API surface you actually expose with APIScout for VS Code and re-test authentication and authorization boundaries through automated API security testing.