PHP2Shell: CVE-2026-109618 and CVE-2026-109619 Explained
Security advisory analysis
PHP2Shell: CVE-2026-109618 and CVE-2026-109619 Explained
Two memory-safety flaws in the PHP runtime let an unauthenticated attacker run code on any server that answers HTTP with PHP. Here is what they are, how they work, and what to do about them.
At a glance
The short version
- Patch to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 and restart php-fpm or Apache. The PHP project says an update is the only complete mitigation.
- Every PHP install that serves HTTP is affected — PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed. Your application code is irrelevant; the flaw is in the runtime.
- Attacks began before disclosure. Amazon Threat Intelligence logged an exploitation attempt on 17 October, four days early. Scope any investigation from that date.
- A WAF is not enough. Cloudflare saw attackers mutating payloads around its new rules within hours.
- Memory disclosure is part of the impact, not just code execution. Treat credentials readable by the PHP process as exposed on any host with indicators.
What is PHP2Shell?
PHP2Shell is the name given to two memory-safety vulnerabilities in the PHP runtime — CVE-2026-109618 and CVE-2026-109619 — disclosed on 21 October 2026. Both are rated CVSS v4.0 9.2 and both allow an unauthenticated attacker to execute code with the privileges of the web worker account.
The critical detail is where the flaws live. They are not in WordPress, not in a framework, and not in anyone’s application code. They are in PHP itself, in the machinery that sets up and tears down an HTTP request. The disclosure site maintained by the researchers states that even a “Hello World” application on an affected PHP version can be vulnerable.
The PHP project’s own announcement is unusually direct:
PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 fix two critical vulnerabilities that can allow unauthenticated remote code execution. All PHP users are strongly encouraged to update immediately.
php.net news, 21 October 2026
Two separate research teams reported the issues: Ryan A. of Calif.io, and Wiz Research together with OpenAI Codex Security. The name PHP2Shell comes from an exploit tool that appeared in screenshots and video when details leaked online the day before the coordinated release.
Which versions are affected
Both CVEs share identical affected ranges. Run php -v on the host, and separately inside each container image, then check against this table.
| Branch | Affected | Fixed in | Notes |
|---|---|---|---|
| 8.5 | < 8.5.12 | 8.5.12 | The only branch with fatal_error_backtraces, which the advisory calls the strongest attack path for CVE-2026-109619. |
| 8.4 | < 8.4.27 | 8.4.27 | |
| 8.3 | < 8.3.36 | 8.3.36 | |
| 8.2 | < 8.2.35 | 8.2.35 | Oldest branch receiving an official release. |
| 8.1, 8.0, 7.4 | All | Source backport only | End of life. The PHP security team published minimal backport branches given the severity. |
| 7.x, 5.4–5.6 | All | No patch | Nothing from the PHP project. The researchers state the vulnerable code paths reach back to PHP 5.4. |
Which runtimes are exposed
Everything that serves HTTP. The CLI is not the attack surface.
| SAPI | Status | Why |
|---|---|---|
| Apache mod_php | Highest risk, confirmed | Invokes request shutdown unconditionally even when startup fails, so the attack completes before application code runs. |
| PHP-FPM | Confirmed | Vulnerable during normal operation. Exits the child on startup failure, which narrows the window without closing it. |
| FrankenPHP | Likely vulnerable | Worker model resembles mod_php. |
| LiteSpeed / LSAPI | Likely vulnerable | Behaves similarly to FPM. |
| php-cgi | Vulnerable | Serves web requests. |
| php-cli | Not the attack surface | No untrusted HTTP request lifecycle. |
Backports for end-of-life branches
Because PHP 8.1 and older no longer receive routine updates, the security team published minimal source backports — the two fixes and nothing else — for people who build from source and have no LTS vendor.
PHP-7.4-CVE-2026-109618-109619 PHP-8.0-CVE-2026-109618-109619 PHP-8.1-CVE-2026-109618-109619
Distribution packages
Most LTS distributions ship backported fixes rather than the upstream version number, so a string like 8.2.35-1~deb12u1 has to be read against the vendor advisory rather than the upstream table above. Debian’s position at the time of writing:
| Release | Package | Fixed version | Advisory |
|---|---|---|---|
| Debian 12 bookworm | php8.2 | 8.2.35-1~deb12u1 | DLA-4848-1 |
| Debian 13 trixie | php8.4 | 8.4.27-1~deb13u1 | DSA-6574-1 |
| Debian sid | php8.4 | 8.4.27-1 | — |
| Debian forky | php8.4 | 8.4.26-1, still vulnerable | — |
The two CVEs in detail
Both records were assigned by the PHP CNA and reserved on 12 October 2026, nine days before release. They carry identical scores and an identical set of weakness classifications.
| Field | CVE-2026-109618 | CVE-2026-109619 |
|---|---|---|
| CNA title | RCE via OOM corruption | RCE via stale global state |
| CVSS v4.0 | 9.2 Critical — CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N, scored by [email protected] | |
| CVSS v3.1 | 9.8 Critical — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, scored by NVD | |
| CWE | CWE-416 use after free, CWE-755 improper handling of exceptional conditions, CWE-824 access of uninitialized pointer, CWE-1325 improperly controlled sequential memory allocation, CWE-1419 incorrect initialization of resource | |
| CAPEC | CAPEC-234, hijacking a privileged process | |
| Published | 2026-10-21 13:48 UTC | 2026-10-21 13:54 UTC |
| Advisory | GHSA-w8hw-w9fx-j79w | GHSA-6wmf-4rv5-p3rp |
Why there are two different scores
The PHP CNA rates attack complexity High with attack requirements Present, which reflects the dependence on heap state and, for CVE-2026-109619, on two requests reaching the same worker process. NVD’s CVSS v3.1 vector has no equivalent field for attack requirements and lands at 9.8. Neither score should change the decision: working exploits exist for both.
The official descriptions
These are the CNA descriptions, verbatim from the CVE records.
PHP uses longjmp() for out-of-memory (OOM) errors to exit the current context and abort handling of the request. Some contexts may not properly account for this scenario and leave request memory in an uninitialized state before performing an allocation. The subsequent request cleanup procedure may read or branch on this memory. Multiple OOM strategies can consistently trigger this condition and lead to remote code execution (RCE).
CVE-2026-109618
PHP has various global state that temporarily points into request memory. In some scenarios, global state may not be correctly reset, or may be repopulated after being reset. The global state may then refer to request memory even after it has been freed. Subsequent requests may read or branch on this stale state before it is fully initialized. Multiple strategies can trigger this condition and produce remote code execution (RCE).
CVE-2026-109619
How the attacks work
Both bugs are variations on one habit: PHP abandoning a request mid-flight and leaving something behind that later code trusts.
CVE-2026-109618: out-of-memory corruption
When an allocation fails, PHP uses longjmp() to jump out of the current context and abort the request. Some of those contexts were never written with that jump in mind. They leave request memory uninitialized before performing an allocation, and the cleanup procedure that runs afterwards reads or branches on it.
That reduces the attacker’s problem to forcing an allocation failure at a moment of their choosing. The advisory lists several ways to do this:
- Multipart requests without a Content-Length header, which bypass the
post_max_sizecheck. - Unlimited headers inside multipart parts, which consume more memory in the parser than their wire representation suggests.
- Empty file controls, which create metadata arrays without counting toward
max_file_uploads.
The fix removes longjmp() from this path entirely. According to the advisory, the patch replaces it with a delayed graceful soft OOM at the next VM safe point, and an immediate hard OOM using _exit() at a higher threshold — so the engine no longer unwinds through partially constructed state.
CVE-2026-109619: stale global state across requests
PHP holds global state — error, session, object and extension state — that temporarily points into memory owned by the current request. When a fatal error or an OOM abandons an operation before that state is reset, the request heap is freed while the worker keeps the reference.
Exploitation requires a pair of requests hitting the same worker. The advisory describes the sequence as:
Request A — the "producer" 1. an operation stores request-owned state globally 2. the state is not reset, often due to a fatal error or OOM 3. request memory is released; the worker retains the reference Request B — the "consumer", same worker 4. the same worker handles another request 5. an operation accesses the stale state before initialization
Running more workers makes the pairing less reliable, but does not prevent it. The advisory names three confirmed producer paths:
- PHP 8.5 fatal error backtraces. With
fatal_error_backtracesenabled, backtrace state survives request boundaries. The advisory calls this “the strongest demonstrated case and the basis of the confirmed mod_php exploitation.” - Session upload progress. This feature, enabled by default, can initialize session state before normal module initialization. An OOM during input processing prevents proper cleanup, leaving stale session and decoder globals for a later upload to reach.
- INI diagnostics. Warnings produced by deprecated or invalid configuration —
auto_detect_line_endings=1, legacyassert.*settings, a malformedmemory_limit— can consume stale error state before request initialization.
The four bug classes behind the working exploits
Wiz Research documented four concrete issues it built exploits on. They compose: the disclosure primitive leaks memory layout, and the corruption primitives then achieve execution.
1. Upload-progress unserializer use-after-free
PHP’s session module can decode session data during upload-progress handling before initialization. A fatal allocation failure interrupts this, destroying the request heap while leaving the unserializer global state BG(unserialize) published. Later requests consume attacker-reclaimed memory as live bookkeeping. Wiz describes the result as a “powerful memory-corruption primitive.”
2. Remote process-memory disclosure
During RFC 1867 multipart parsing at request startup, session upload-progress can initialize state and publish a session ID in PS(id). An out-of-memory error aborts before cleanup, leaving PS(id) pointing at a freed allocation. Subsequent requests trust the stale pointer and URL-encode reclaimed heap contents into PHPSESSID response cookies.
Wiz notes this path is application-agnostic: it works even when the application never calls session_start(), and can disclose worker memory containing credentials from other requests.
3. Fatal-error backtrace use-after-free
PHP retains fatal error backtraces in EG(last_fatal_error_backtrace). An out-of-memory error destroys the request heap without undefining this value, and workers emit diagnostics that access the pointer before the executor reinitializes — a cross-request use-after-free.
4. OPcache type confusion
During zend_accel_script_persist(), PHP advances the process-local CG(map_ptr_last) frontier before publishing the shared frontier. Allocation failures between those two events create mismatches where workers reuse slots for different object types, corrupting shared OPcache state across workers.
What researchers demonstrated
Wiz reported pre-authentication remote code execution against stock container images, with no plugins and no misconfiguration required.
| Target | Chain used |
|---|---|
| wordpress:latest | Upload-progress use-after-free and OPcache type confusion |
| drupal:latest | Fatal-error backtrace use-after-free |
| joomla:latest | Upload-progress use-after-free |
| Any PHP application | Application-agnostic memory disclosure |
The impact described by the researchers runs wider than code execution: attackers can execute code without authentication, steal sensitive memory such as passwords, tokens and API keys, or disclose data without executing code at all.
Disclosure timeline
The coordinated release did not hold. Attacks preceded public disclosure by four days, and the release itself was pulled forward by one after details leaked.
| When | What happened |
|---|---|
| 19–24 Aug 2026 | A single Wiz researcher goes from discovery to working exploits in one week. |
| 12 Oct 2026 | PHP reserves both CVE IDs. |
| 17 Oct 2026 | Amazon’s MadPot honeypot records an exploitation attempt from a commercial anonymization network — four days before disclosure. |
| 20 Oct 2026 | The CVE IDs leak on Hacker News and social media, including screenshots and video of an exploit tool named PHP2Shell. PHP issues advance notice and moves the release forward a day. |
| 21 Oct, 02:00 UTC | Earliest proof-of-concept exploitation identified by AWS, twelve hours before the embargo lifts. |
| 21 Oct, 02:00–08:00 UTC | Further attempts from additional anonymized infrastructure, with payloads varied enough that AWS assesses multiple distinct actors. |
| 21 Oct, 03:02 UTC | Cloudflare deploys WAF protections to all plans including free, roughly nine hours ahead of disclosure. |
| 21 Oct, 05:21 UTC | Palo Alto Networks ships Threat ID 543004 virtual patching. |
| 21 Oct, 13:48 / 13:54 UTC | The two CVE records publish. |
| 21 Oct, 14:00 UTC | Embargo lifts. PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 are released with source backports for 7.4, 8.0 and 8.1. |
| 21 Oct 2026 | CISA publishes an alert urging immediate patching. The NSA issues its own advisory. |
| 21–22 Oct 2026 | Cloudflare reports millions of exploit attempts. Debian ships DSA-6574-1 and DLA-4848-1. |
Exploitation in the wild
This is not a theoretical advisory. The evidence from multiple independent vendors:
- Amazon Threat Intelligence identified an exploitation attempt in MadPot telemetry dated 17 October 2026, four days before public disclosure, originating from a commercial anonymization network IP.
- Cloudflare reports millions of exploit attempts, including pre-disclosure activity, and observed attackers mutating payloads to try to bypass its protections.
- watchTowr observed exploitation in the wild as of 21 October through its Attacker Eye honeypot network.
- Palo Alto Cortex Xpanse identified more than 22.6 million exposed, potentially vulnerable instances.
- Unit 42 reports that at least one European government took its website offline as a precaution.
Scope your investigation from 17 October, not from the 21st. AWS explicitly recommends investigating activity from 17 October forward rather than from the disclosure date, and advises assuming exploitation predates public disclosure.
Indicators of compromise
Amazon Threat Intelligence published these source addresses. AWS notes they include shared anonymization infrastructure and recommends investigating rather than automatically blocking.
| IP address | First observed |
|---|---|
| 103.216.220.92 | 2026-10-17 (pre-disclosure) |
| 31.223.184.166 | 2026-10-21 |
| 37.19.200.5 | 2026-10-21 |
| 134.209.159.64 | 2026-10-21 |
| 103.114.163.188 | 2026-10-21 |
| 95.217.98.37 | 2026-10-21 |
How to detect it
Wiz cautions that these issues execute below the application layer, so application logs may contain little context. Detection has to work on infrastructure behaviour.
Signals to look for
- POST requests containing
PHP_SESSION_UPLOAD_PROGRESSin the form body. AWS WAF guidance matches this string in POST bodies combined with aPHPSESSIDin the body or cookie. - Unusual multipart requests followed by memory-limit errors.
- Unexpectedly large or malformed
PHPSESSIDresponse cookies — the visible form of the memory-disclosure primitive. - Repeated HTTP 500 responses and unexplained PHP worker churn.
- Unexpected process execution on web servers and unexplained file modifications on application servers.
The commands below are example searches for the indicators above; adjust paths for your own deployment.
# POSTs carrying the upload-progress marker grep -rl 'PHP_SESSION_UPLOAD_PROGRESS' /var/log/nginx /var/log/apache2 # memory exhaustion clustered in time grep -i 'Allowed memory size' /var/log/php*-fpm.log | tail -50 # FPM children dying unexpectedly grep -iE 'exited on signal|SIGSEGV|child .* exited' /var/log/php*-fpm.log
Prefer behavioural detections over CVE-specific signatures. This is AWS’s explicit recommendation, and Cloudflare’s observation of payload mutation within hours of rule deployment supports it.
Vendor coverage
| Vendor | What shipped |
|---|---|
| Cloudflare | Deployed to all plans including free from 21 Oct 03:02 UTC. Managed rules “Generic Rules – Anomalous Execution 1–7”: free ruleset 77454fe2d30c4220b5701f6fdfb893ba, paid efb7b8c949ac4650a09736fc376e9aee, response firewall rule afffe22785674c80a31875c848d2242a. Uses full transaction detection, combining request and response signals. Paid customers should confirm the ruleset is in block mode. |
| AWS | PHP Managed Rule Group v2.3. Guidance matches POSTs containing PHP_SESSION_UPLOAD_PROGRESS plus PHPSESSID; CRS SizeRestrictions_BODY for the OOM path. |
| Palo Alto Networks | Threat ID 543004 via Advanced Threat Prevention Plus with Frontier Virtual Patching. Cortex XDR/XSIAM post-exploitation protection; Cortex Xpanse for exposure discovery. |
| Microsoft | Azure WAF mitigations and upgrade guidance. |
| Rapid7 | Remote unauthenticated checks in Exposure Command, Vulnerability Management and Nexpose. Velociraptor artifact Generic.Applications.PHPInventory. |
Request size limits do not stop CVE-2026-109619. The exploit body for the session path is roughly 450 bytes, so size-based WAF rules are ineffective against it. They are only relevant to the OOM path.
How to fix it
Upgrade the runtime and restart the workers. A running php-fpm pool keeps the old binary mapped until it restarts, so a package upgrade alone is not sufficient. Container images must be rebuilt and redeployed.
php -v # CLI, not necessarily what serves HTTP php-fpm -v # the binary that matters systemctl restart php8.3-fpm systemctl restart apache2 # for mod_php # fixed releases: 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35
Updating to a PHP release containing the patch to this vulnerability is the only complete mitigation.
GHSA-w8hw-w9fx-j79w
Partial workarounds
The PHP team states that the measures below reduce exposure to specific attack paths but do not make an installation safe. For CVE-2026-109619 specifically, the advisory warns there is no complete generic configuration mitigation, because an application, extension or server path may publish stale state or consume it.
For CVE-2026-109618, the OOM path
php_admin_value[memory_limit] = -1 php_admin_value[post_max_size] = 8M php_admin_value[max_input_nesting_level] = 32
- Keep
post_max_sizeconservative and enforce it at the frontend. - Reduce
max_input_nesting_leveland bound input variables, parts and headers. - Block multipart requests at the frontend if the application does not use them.
- Consider container or cgroup memory limits — the advisory specifically says not
RLIMIT_ASorRLIMIT_DATA.
For CVE-2026-109619, the stale-state path
session.upload_progress.enabled = Off fatal_error_backtraces = 0 # PHP 8.5 only pm.max_requests = 1 # if performance is not critical
- Eliminate fatal errors on attacker-reachable paths.
- Remove diagnostics from INI configuration applied before request initialization.
What does not work
- Hiding diagnostics. The advisory is explicit: suppressing output through
error_reporting,display_errors,log_errorsor@does not prevent stale-state access. - Request size limits alone, against CVE-2026-109619.
- Updating your application, CMS or plugins. The flaw is in the runtime beneath them.
- Patching the host without rebuilding container images or restarting workers.
- Relying on a WAF instead of patching. Cloudflare advises patching even with its WAF enabled.
If you have been compromised
Because one of the primitives discloses worker memory rather than just executing code, the impact is not limited to the application. A worker’s heap can contain credentials from other requests.
- Rotate credentials. AWS advises that credentials readable by the PHP process should be assumed exposed on any host with indicators.
- Investigate from 17 October 2026 forward, not from the disclosure date.
- Check for unexpected process execution and file modifications on application servers, which is AWS’s stated detection guidance.
- Review web server logs for the suspicious POST activity described in the detection section above.
The AI angle
The vulnerable code existed for over a decade. What changed, according to Wiz, was not the code but the cost of understanding it.
Wiz reports that its research used Wiz Atlas alongside Gemini 4 Argon and GPT-5.6 Sol, and credits Gemini 4 Argon with contributions to finding the vulnerability, developing the exploit, and attack detection and validation. The PHP Foundation credits Calif, OpenAI and Wiz, both of the latter having used AI models in the research.
Wiz’s framing of why this matters:
Vulnerabilities that once seemed impractical to exploit become viable targets.
Wiz Research
The specific barrier that fell was adaptation. Wiz notes that differences in PHP versions, configurations and application behaviour previously required substantial manual adjustments to an exploit; AI enabled automated adaptation across targets through iterative testing and debugging. The result was that a single researcher completed, in one week between 19 and 24 August 2026, what Wiz describes as previously taking a world-class team months.
Frequently asked questions
What is PHP2Shell?
PHP2Shell is the name given to two memory-safety vulnerabilities in the PHP runtime, CVE-2026-109618 and CVE-2026-109619, disclosed on 21 October 2026. Both are rated CVSS v4.0 9.2 and allow an unauthenticated attacker to execute code as the web worker account on any server that serves HTTP requests with PHP.
Which PHP versions are affected?
PHP 8.2 before 8.2.35, PHP 8.3 before 8.3.36, PHP 8.4 before 8.4.27, and PHP 8.5 before 8.5.12. The researchers state the vulnerable code paths reach back to PHP 5.4, so PHP 5, 7, 8.0 and 8.1 are also affected but receive no official release.
How do I fix PHP2Shell?
Upgrade to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 and restart php-fpm or Apache. The PHP project states that updating to a patched release is the only complete mitigation. Updating your application, CMS or plugins does not fix it, because the flaw is in the PHP runtime itself.
Is PHP2Shell being exploited in the wild?
Yes. Amazon Threat Intelligence recorded an exploitation attempt in its MadPot honeypot network on 17 October 2026, four days before public disclosure. Cloudflare reports millions of exploit attempts, and watchTowr observed exploitation through its honeypot network on 21 October 2026.
Does a WAF protect me?
Only partially. Cloudflare, AWS, Palo Alto Networks and Microsoft shipped rules, but Cloudflare observed attackers mutating payloads to bypass its protections within hours of deployment. Cloudflare’s own guidance is to patch even with the WAF enabled.
Size-based rules are particularly limited: the exploit body for the session path is roughly 450 bytes.
My app does not use file uploads or sessions. Am I safe?
No. Wiz Research states the memory-disclosure path is application-agnostic and works even when the application never calls session_start(). The session.upload_progress feature is enabled by default and runs during request parsing, before application code executes.
Is PHP 5 or PHP 7 going to get a patch?
PHP 5 will not. For PHP 7.4, 8.0 and 8.1 the security team published minimal source backport branches in php-src given the severity, but these branches do not receive routine updates. Some distributions and LTS vendors provide backported fixes, so the PHP project advises contacting your vendor or upgrading to a supported version.
Is PHP2Shell in CISA’s Known Exploited Vulnerabilities catalog?
Not as of KEV catalog version 2026.10.21. CISA did publish a standalone alert on 21 October 2026 urging immediate patching, and the NSA issued its own advisory. Given the documented in-the-wild exploitation, check the live catalog for current status.
Sources
Every factual claim in this post comes from one of the following. Primary records were read directly from the issuing authorities.
Primary
- NVD — CVE-2026-109618
- NVD — CVE-2026-109619
- GHSA-w8hw-w9fx-j79w — RCE via OOM corruption
- GHSA-6wmf-4rv5-p3rp — RCE via stale global state
- php.net news archive, 21 October 2026
- php2shell.com — researchers’ disclosure site
- Debian security tracker
- CISA alert, 21 October 2026
- CISA Known Exploited Vulnerabilities catalog
- NSA advisory
- oss-security announcement
Research and threat intelligence
- Wiz Research — technical writeup
- Amazon Threat Intelligence — MadPot
- Cloudflare — WAF protections
- AWS security bulletin 2026-143
- Unit 42 threat brief
- Rapid7 emergency threat response
- watchTowr FAQ
- Tenable FAQ
- Huntress rapid response
- Google Cloud response guidance
- Microsoft — Azure WAF mitigations
- Palo Alto Networks product impact
- Check Point analysis
- Securelist analysis
- SecurityWeek
- Armis — finding and patching affected systems
- Plesk advisory