A critical PHP flaw (CVE-2026-109618, CVSS 9.2) lets unauthenticated attackers turn out-of-memory errors into remote code execution and heap memory leaks. Apache mod_php and FrankenPHP are most exposed. Patch to 8.2.35, 8.3.36, 8.4.27 or 8.5.12 now.

PHP's out-of-memory handling has a bug that lets attackers take over servers. The PHP project patched it on October 21, 2026, as CVE-2026-109618. An unauthenticated attacker can send crafted HTTP requests that exhaust PHP's memory budget at the wrong moment. Those requests corrupt the interpreter's internal state, and on some setups the attacker can run their own code on the server. Wiz Research named the bug "PHP2Shell" and says it privately demonstrated full remote code execution against default WordPress, Drupal and Joomla installs. Because the flaw sits in PHP itself, nearly any internet-facing PHP server running an unpatched 8.2 through 8.5 release should be treated as at risk until it is updated.
On October 21, 2026, the PHP Group published security advisory GHSA-w8hw-w9fx-j79w. It was released alongside fixed builds for every supported branch: PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12. The CVE record, titled simply "RCE via OOM corruption," carries a CVSS v4.0 base score of 9.2 (Critical). The score's vector string is 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. In plain terms, that means:
The credits list several parties:
The CVE record lists the discovery source as "UNKNOWN." Wiz's write-up, however, gives a timeline: the flaw was discovered on August 19, 2026, and the vendor was notified on August 24, 2026. That left roughly two months between the report and coordinated public disclosure.
The U.S. Cybersecurity and Infrastructure Security Agency (CISA) added its own assessment to the record within hours of publication. Its SSVC triage data, timestamped October 21, 2026 at 14:34 UTC, rates the bug as follows:
That is a fair summary of where things stand. Nobody has been seen exploiting this flaw in the wild, and no public exploit code exists. But if someone does exploit it, the attacker gets the keys to the box.
Wiz also makes a claim about the bug's age. It says the underlying weakness has existed since PHP 5.4. The official CVE record, though, lists only the currently supported 8.2 through 8.5 branches as affected. Systems running end-of-life PHP versions will not get a patch at all.
The bug starts with how PHP handles running out of memory. Every PHP request runs under a memory ceiling, set by the memory_limit setting in php.ini. When a script, or PHP's own request-parsing code, tries to allocate memory beyond that ceiling, PHP does not return an error and carry on. Instead it calls longjmp().
longjmp() is a C library function that acts like an emergency exit. It jumps straight out of whatever code is running and lands at an earlier checkpoint, one that was saved with setjmp(). Every function in between is abandoned mid-step. PHP uses this to abort the doomed request and move on to cleanup.
Here is the problem. According to the PHP advisory, some parts of PHP make request memory reachable before that memory is fully set up. Imagine a moving crew that puts an empty box on the shelf and labels it "tax documents" before putting anything inside. If the fire alarm goes off at that moment, everyone leaves. Later, the cleanup crew walks in, trusts the label and starts handling whatever junk is in the box.
The PHP advisory describes it this way:
"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)."
The following simplified C sketch shows the general class of bug. It is not actual php-src code. It only illustrates why a non-local jump during setup is dangerous:
1/* SIMPLIFIED ILLUSTRATION ONLY -- not real php-src code */2#include <setjmp.h>34static jmp_buf bailout;5static struct state *global_state; /* reachable by shutdown code */67void *alloc_or_bail(size_t n) {8 if (over_memory_limit(n))9 longjmp(bailout, 1); /* OOM: jump out immediately */10 return real_alloc(n);11}1213void startup(void) {14 global_state = alloc_or_bail(sizeof *global_state);15 /* global_state is now published, but its fields are garbage... */16 global_state->buf = alloc_or_bail(huge_attacker_controlled_size);17 /* ...if THIS allocation fails, we never reach the init below */18 global_state->len = 0;19 global_state->ptr = NULL;20}2122void shutdown(void) {23 if (global_state && global_state->ptr) /* branches on garbage */24 free_thing(global_state->ptr); /* operates on garbage */25}2627void handle_request(void) {28 if (setjmp(bailout) == 0)29 startup();30 shutdown(); /* runs unconditionally, even after a failed startup */31}The CVE record maps this behavior to five weakness categories, plus one attack pattern:
ID | Name |
|---|---|
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-234 | Hijacking a Privileged Process |
Wiz's research goes further than the vendor advisory. It points to PHP's Session module and its RFC1867 upload-progress feature. RFC1867 is the standard behind HTML file uploads sent as multipart/form-data. The upload-progress feature lets PHP track how much of a file upload has arrived, and it does this by writing progress data into the user's session.
That tracking happens very early in the request, in what Wiz calls a "pre-initialization window." Wiz says it can run even when the application never calls session_start(). During this window, PHP decodes session data and keeps temporary bookkeeping in a global structure called BG(unserialize).
When an out-of-memory error hits during that window, longjmp() tears down the request heap before PHP clears the relevant global pointers. Wiz identified two leftover pointers that become dangerous:
BG(unserialize) stays published. It points at the unserializer's bookkeeping in memory that has already been freed.PS(id), the session ID pointer, can be left pointing at freed heap memory after early multipart parsing is aborted.PHP workers are long-lived processes that handle request after request, so these stale pointers survive into the next request handled by the same worker. That gives an attacker two building blocks:
PS(id) pointer and its length as a live string, the internal type zend_string. It then URL-encodes whatever now occupies that memory and sends it back to the attacker in the PHPSESSID response cookie. In effect, the server mails out a chunk of its own memory. That memory can contain passwords, tokens and API keys. It also contains addresses that defeat ASLR (Address Space Layout Randomization, the defense that scatters code and data to unpredictable memory locations).BG(unserialize). Combined with the addresses already leaked, this leads to arbitrary code execution.Wiz says it built a private exploit script called php2shell that chains both steps. No public proof-of-concept code has been released.
PHP already limits uploads through settings like post_max_size, max_file_uploads and max_input_nesting_level. So how does an attacker reliably run out of memory at exactly the wrong moment? The PHP advisory lists several ways to make a request use far more memory than those limits suggest:
Content-Length header on multipart requests, which sidesteps some size checks.max_file_uploads and inflate the internal arrays PHP builds to describe uploaded files.The predictable per-request memory_limit actually helps the attacker here. It lets them aim the out-of-memory failure at a specific allocation.
Combining the PHP advisory, the Openwall oss-security disclosure and Wiz's research, the attack proceeds like this:
memory_limit. An allocation fails at a moment when request memory is reachable but not yet fully set up. In the Wiz chain, this happens during upload-progress session decoding.longjmp() to abort the request. This leaves request memory uninitialized or invalid, including the stale BG(unserialize) and PS(id) pointers.PHPSESSID cookie and returns it.The PHP developers did not try to patch each dangerous code path one at a time. They removed longjmp() from out-of-memory handling entirely and replaced it with two tiers:
_exit(). No cleanup code runs, so no cleanup code can trip over corrupted state.This fixes the root design flaw rather than individual symptoms. It also means some behavior around memory exhaustion will change on patched systems.
Vendor | Product | Package | Affected Versions | Fixed Versions |
|---|---|---|---|---|
PHP Group | PHP | php-src | 8.2.* prior to 8.2.35 | 8.2.35 |
PHP Group | PHP | php-src | 8.3.* prior to 8.3.36 | 8.3.36 |
PHP Group | PHP | php-src | 8.4.* prior to 8.4.27 | 8.4.27 |
PHP Group | PHP | php-src | 8.5.* prior to 8.5.12 | 8.5.12 |
All four fixed releases shipped on October 21, 2026. Versions outside these ranges are listed as unaffected by default in the CVE record. Wiz, however, says the underlying flaw dates back to PHP 5.4. Anyone running an end-of-life branch (8.1 and earlier) should assume they are vulnerable and will not receive a fix.
The biggest factor in real-world risk is the SAPI, or Server API: the layer that connects PHP to the web server. The PHP advisory describes each one:
SAPI | Exposure | Why |
|---|---|---|
Apache mod_php | Highest | The request shutdown procedure runs unconditionally even when request startup fails. Exploitation is application-independent and happens before any PHP application code runs. Wiz privately validated full unauthenticated RCE against mod_php with default PHP input and memory limits and a normal multi-worker "prefork" setup. |
FrankenPHP | Highest | Same unconditional shutdown behavior as mod_php. Considered susceptible to application-independent exploitation, though not fully validated in the analyzed sources. |
PHP-FPM | Elevated | FPM terminates the child worker when request startup fails, which blocks the startup-phase attack. Out-of-memory triggers after startup remain possible but need application-specific conditions. The Openwall disclosure says source analysis suggests FPM is susceptible, but no complete FPM exploit had been shown at disclosure. |
LSAPI (LiteSpeed) | Elevated | Shares PHP-FPM's model. Application-specific exploitation is considered likely but was not fully validated. |
PHP runs WordPress, Drupal and Joomla, along with countless custom applications, shared hosting platforms and enterprise intranets. Wiz's demonstration against default installs of all three CMS platforms matters because the attack does not depend on a bug in the application. If the server runs vulnerable PHP, the application on top does not help.
As of publication on October 21, 2026:
php2shell script has only been described, not released.The sources disagree somewhat on how hard exploitation is. The PHP advisory and the Wiz analysis rate it "difficult." The Openwall disclosure rates it "moderate." The CVSS vector marks it high complexity with attack requirements present. Reliable exploitation means steering heap memory layout across several requests to the same worker, which takes skill.
Still, there are reasons not to relax. Wiz has shown the full chain works. The PHP advisory says multiple out-of-memory strategies trigger the condition "consistently." And the patches themselves show attackers exactly where to look. PHP is one of the most heavily targeted technologies on the internet, so it is reasonable to expect exploit developers to try to reproduce this work quickly.
An attacker who succeeds runs code as the web worker account. The sources describe these possible outcomes:
The memory disclosure step is a serious threat on its own. Even an attacker who never gets code execution could repeatedly dump live worker memory through the PHPSESSID cookie. That memory may hold other users' passwords, tokens and session data as they pass through the process.
The worst case looks like a shared hosting provider or large CMS fleet running Apache mod_php, the most exposed setup. One attacker with a working exploit could compromise every site on every vulnerable server, with no WordPress plugin bug or stolen password required. Because the attack happens before any application code runs, application-level security plugins never see it.
The PHP advisory is clear: updating to a fixed release is the only complete mitigation.
If you run... | Upgrade to at least... |
|---|---|
PHP 8.2.x | 8.2.35 |
PHP 8.3.x | 8.3.36 |
PHP 8.4.x | 8.4.27 |
PHP 8.5.x | 8.5.12 |
First, check your version and SAPI:
1# CLI version (may differ from the web-facing build!)2php -v34# Is Apache loading mod_php?5apachectl -M 2>/dev/null | grep -i php67# Which PHP-FPM versions are installed/running?8ls /usr/sbin/php-fpm* 2>/dev/null9systemctl list-units --type=service | grep -i fpmThe command-line php binary may be a different build from the one your web server uses. Confirm the version that actually serves web traffic by checking phpinfo() output in a restricted, temporary file, or through your configuration management inventory.
If your Linux distribution backports fixes, check its security tracker for the package version that includes the fix for CVE-2026-109618, since the version string may not match upstream. After upgrading, restart every PHP worker: Apache, PHP-FPM pools, FrankenPHP and LSAPI processes. Old worker processes keep running the vulnerable code until they are restarted.
The PHP advisory lists several stopgaps. All of them are partial, and all of them have real operational costs.
a) Remove the predictable memory ceiling, and back it with an OS-level cap. Setting memory_limit=-1 removes the per-request limit that lets attackers place the out-of-memory failure precisely. You must enforce it everywhere. Restart all workers and make sure no FPM pool, directory, .htaccess file or application setting overrides it.
1; php.ini -- partial mitigation only; pair with OS-level memory limits2memory_limit = -1For PHP-FPM, php_admin_value stops scripts from overriding the setting:
1; /etc/php/8.x/fpm/pool.d/www.conf2php_admin_value[memory_limit] = -1For Apache mod_php, php_admin_value in the server config stops .htaccess from overriding it:
1php_admin_value memory_limit -1Side effect: PHP processes can now use unlimited memory, which raises the risk of the whole host running out of memory or being knocked offline. Pair this with container or cgroup memory limits.
b) Use cgroup or container memory limits (memory.max) instead of PHP's internal limit. Make sure you understand what happens to workers when the limit is hit.
1# systemd drop-in, e.g. /etc/systemd/system/php8.3-fpm.service.d/memory.conf2[Service]3MemoryMax=2GSide effect: hitting the limit can kill entire worker processes or containers and take services down.
c) Do NOT use RLIMIT_AS or RLIMIT_DATA. The advisory warns specifically that these limits make mmap() fail once reached, which triggers the vulnerable longjmp() path directly. Remove them if they are set.
1# Check for address-space / data-segment limits on PHP workers2for pid in $(pgrep -f 'php-fpm|apache2|httpd|frankenphp'); do3 grep -E 'Max address space|Max data size' /proc/$pid/limits4done5# Both should read "unlimited"d) Keep post_max_size conservative, and separately validate and enforce the decoded request size at your reverse proxy or front end. Side effect: legitimate large uploads may be rejected.
1# nginx front end example2client_max_body_size 8m;1# Apache example2LimitRequestBody 8388608e) Lower max_input_nesting_level and cap the number of input variables, multipart parts, headers per part and file uploads. Side effect: complex forms and legitimate uploads may break, so test compatibility first.
1max_input_nesting_level = 162max_input_vars = 10003max_file_uploads = 5f) Block multipart/form-data at the front end if your application does not need it. Side effect: file uploads and any forms using multipart encoding will stop working.
1# nginx: reject multipart bodies on sites that never accept uploads2if ($content_type ~* "multipart/form-data") {3 return 403;4}g) Turn on WAF/CDN protections. Wiz notes that providers including Cloudflare, AWS, Fastly, Akamai, Imperva and Google Cloud offer built-in rules to filter malicious multipart upload-progress requests. Side effect: legitimate uploads or upload-progress features may be blocked, so test first.
h) Runtime protection. Wiz says Wiz Sensor version 1.0.12768 or newer includes response policies that automatically kill processes showing behavior typical of PHP2Shell exploitation. Side effect: false positives may cause brief service interruptions.
i) Review the related advisory GHSA-6wmf-4rv5-p3rp, which the PHP advisory references, and apply its guidance where relevant.
No official YARA or Sigma detection rules have been published. The PHP advisory and Wiz research point to these signs of attempted exploitation:
Network and request patterns:
multipart/form-data requests missing a Content-Length headermax_file_uploadsResponse anomalies:
PHPSESSID cookies in responses, especially ones full of percent-encoded data that looks like raw memory. This is a strong sign of the memory-leak step.Server and log indicators:
Allowed memory size ... exhausted, especially during request startup and before any application code runspool www children exiting after memory exhaustion or with a segmentation fault signalStarter hunting commands:
1# Memory exhaustion events in PHP / web server logs2grep -i "allowed memory size" /var/log/apache2/error.log /var/log/php*-fpm.log 2>/dev/null34# Segfaulting workers5grep -iE "segmentation fault|SIGSEGV|exited on signal 11" \6 /var/log/apache2/error.log /var/log/php*-fpm.log 2>/dev/null78# Top sources of POST traffic (look for single IPs hammering upload endpoints)9awk '$6 ~ /POST/ {print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | headCheck any exposed PHP system that shows unexplained memory exhaustion or worker crashes for signs of compromise. The memory disclosure step can leak secrets without leaving obvious traces, so Wiz recommends that after containment you rotate the credentials, tokens and API keys on affected systems. That includes database passwords, API secrets, session signing keys and any TLS private keys the worker account could read.
PHP2Shell is a useful lesson in how error handling can become the vulnerability. longjmp()-based bailouts have been part of PHP's engine since at least the 5.x era. The design was simple: when things go wrong, jump out and clean up. It also depended on every piece of code being ready to be abandoned at any allocation, which turned out not to be true. The fix shows the PHP team agreed the design was the problem. They replaced the emergency exit with a two-tier system that either waits until the interpreter is in a safe state or ends the process with no cleanup at all.
The credits are also worth noting. OpenAI is listed as the finder of this bug, with Calif.io and Wiz as reporters. Automated and AI-assisted tools are increasingly finding subtle memory-safety flaws in mature, heavily reviewed code. Defenders should expect more bugs from deep, decades-old engine code that human reviewers have long missed.
php2shell. Watch for independent reproductions now that patches can be compared against the old code.The practical advice is simple even though the bug is complex. Patch to 8.2.35, 8.3.36, 8.4.27 or 8.5.12, restart every worker, and start with Apache mod_php and FrankenPHP servers. In the meantime, watch for oversized PHPSESSID cookies.