CISA KEV PHP Runtime Active Exploit

PHP2Shell turns a failed allocation into a shell before your code runs

November 2, 2026·8 min read·CVEasy Threat Research
A green worker ring carries two request blocks; the first is cut short by a gold jump arc, leaving a freed dashed heap slab that a gold stale pointer still targets while a mint second request reads through it

The evidence on a compromised box is thin. An Apache error log shows two or three Allowed memory size of ... bytes exhausted fatals reported at Unknown on line 0, which means PHP ran out of memory before any script was even loaded. A few seconds later, the www-data user spawns /bin/sh. Datadog Security Labs describes this footprint, and it is how most teams will find out whether the October PHP2Shell bugs reached them.

The two flaws are CVE-2026-109618 and CVE-2026-109619. PHP fixed both on October 21, 2026 in 8.5.12, 8.4.27, 8.3.36 and 8.2.35, rating each 9.2 under CVSS 4.0 (GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp). Amazon's MadPot honeypots recorded exploitation attempts on October 17, four days before the release, and CISA added both CVEs to the Known Exploited Vulnerabilities catalog on October 26 with a federal deadline of October 29, as BleepingComputer and The Record reported. A week later, the useful questions are which of your PHP hosts are in the exploitable shape and which only look patched.

Out of memory, longjmp, and a half-built hash entry

PHP handles a failed allocation against memory_limit by raising a fatal error and calling longjmp() straight out of whatever it was doing. That design assumes request memory is either fully built or unreachable when the jump happens. The advisory for CVE-2026-109618 shows that assumption fails. In some code paths request memory becomes reachable before it is initialized, so a jump at the wrong instant leaves a structure half built, and request shutdown later reads and branches on it. Datadog gives the concrete case: the hash table insert updates its counters before allocating the new key, so if that allocation fails, cleanup walks an entry it believes is valid. Because the failed allocation can be anywhere, Datadog advises against defenses tied to the hash table specifics.

An attacker therefore needs to make PHP run out of memory at a chosen moment without exceeding the obvious size limits. The advisory lists three ways the multipart parser allows it. A multipart body without a usable declared length can pass the post_max_size check and keep feeding decoded data. A single part can carry an unlimited number of headers, which cost far more memory inside PHP than on the wire. Empty file controls do not count toward max_file_uploads but still allocate metadata arrays, and nested bracketed field names multiply that work. All of this happens during automatic body parsing, before your application sees the request.

The request that dies and the request that pays

CVE-2026-109619 is the same failure seen from the other side. A PHP worker keeps request-owned state (error, session, object and extension globals) in memory that outlives the request, and that state has to be reset before the request heap is freed. If a fatal error abandons an operation before its reset runs, the global keeps pointing into a heap that is about to be handed back. The next request on the same worker then reads the stale pointer during early processing, before normal initialization overwrites it. One request produces the dangling state and a later one consumes it, a use-after-free split across two HTTP requests.

The advisory confirms two consumer paths. The first exists only on PHP 8.5 with fatal_error_backtraces enabled: the stored backtrace is never cleared, and a diagnostic raised while Apache applies .htaccess or php_value directives (which happens before php_request_startup()) reads it. Session upload progress is the second path, where a multipart upload that includes a PHP_SESSION_UPLOAD_PROGRESS field initializes session and decoder state before module initialization, an out-of-memory error cuts the request short after that state is published, and the next upload progress operation trusts the dangling globals. Upload progress is enabled by default and runs even when the application never calls session_start(), so a plain WordPress or Laravel site exposes it without anyone choosing to.

Why prefork Apache is the worst case. The advisory says mod_php calls request shutdown unconditionally, even when startup failed, and it demonstrated full remote code execution over HTTP against mod_php with default limits. PHP-FPM kills the child when startup fails, which narrows CVE-2026-109618, but the upload progress path of CVE-2026-109619 was confirmed on both mod_php and FPM.

Where a 9.2 with high attack complexity lands in a CVSS queue

Both vectors read AV:N/AC:H/AT:P, and a queue sorted on base score files them among dozens of other 9s on internal software nobody can reach. Exploit reliability does depend on the build and the allocator layout, as the advisory admits, and CISA's listing shows attackers are making the attempts anyway.

Scanners make the ranking worse in two ways. The flaw sits in the interpreter under the application, so composer audit and WordPress plugin scanners report nothing. Version checks also mislead in both directions. Distribution packages often carry the backport without changing the upstream version string, a gap Datadog also warns about, while a host that installed the fixed package but never restarted its workers reports the new version and keeps running the old code. Neither signal tells you whether upload progress is on, which SAPI is serving, or whether the host takes multipart traffic from the internet, and those facts decide whether a finding needs action today.

How TRIS splits one CVE across three PHP hosts

TRIS, the Threat and Risk Intelligence Scoring engine inside CVEasy AI, scores this finding on the evidence and on the host it sits on, and four layers do most of the work.

Exploitation evidence. KEV inclusion on October 26 and honeypot attempts dated October 17 put both CVEs in confirmed in-the-wild territory. Public exploit repositories also exist, and TRIS records them separately, because a working PoC proves feasibility while KEV is the confirmation of real abuse. No public source has given a reliable count of compromised production servers.

Exposure path. An internet-facing Apache prefork host running mod_php is the shape the advisory proved end to end. The same CVE on an FPM pool behind a reverse proxy that rejects multipart bodies has a much narrower path.

Configuration reachability. TRIS reads the collected session.upload_progress.enabled, fatal_error_backtraces and memory_limit values and the pm.max_requests setting. A worker that serves one request and exits cannot carry stale state into a second request, so it removes the CVE-2026-109619 consumer outright.

Blast radius. Code execution lands as the web worker account, which on most PHP stacks can read wp-config.php or a Laravel .env with database and mail credentials in it.

The result is one CVE with three scores. The exposed mod_php host with upload progress on lands in the top ACT band. An FPM pool behind a filtering proxy drops to a scheduled patch, and a sandboxed single-request worker with upload progress off scores far lower again, even though all three report the same vulnerable version.

Fixed builds, interim settings, and the log lines to hunt

Upgrade to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or to a distribution package whose changelog names both CVEs, then restart every worker so no old child keeps the vulnerable code in memory. PHP published minimal source backports for 7.4, 8.0 and 8.1, which are otherwise out of support. Both advisories say updating is the only complete fix.

Until the upgrade is in place, set session.upload_progress.enabled=0 and, on 8.5, fatal_error_backtraces=0 in configuration that loads before request parsing. Keep upload progress off after patching if nothing uses it, since on October 23 PHP also warned of a separate session upload progress issue, CVE-2026-115560, that the October 21 releases do not fix (The Hacker News). Block multipart requests at the frontend where the application does not take uploads, keep post_max_size and max_input_nesting_level low, and do not use RLIMIT_AS or RLIMIT_DATA, which make mmap() fail and trigger the vulnerable jump.

For the hunt, start from Datadog's indicators: multipart fields named PHP_SESSION_UPLOAD_PROGRESS or PHPSESSID on applications that never use them, very large filler fields, deeply nested bracketed names, upload filenames containing <?php or |O:, unusually long or binary Set-Cookie: PHPSESSID= values, bursts of memory fatals at Unknown on line 0 on one worker, AH00052 segfault lines, and any shell whose parent is a web or PHP process.

A shell under the web user means rotation. If you find a shell spawned by a PHP worker, rotate every secret that worker could read, including database passwords, SMTP credentials, API keys in .env or wp-config.php, and any cloud keys mounted into the container, then rebuild the host instead of cleaning it.

How CVEasy AI surfaces this

CVEasy AI, the number one local-first CTEM platform, ingests the PHP advisories, the KEV listing and research such as Datadog's write-up, then runs them through TRIS against the PHP inventory it has collected on your own hardware. It answers the questions that decide this week: which hosts run mod_php or FPM, which have upload progress on, which accept multipart traffic from the internet, and which report a fixed version but still run old workers. Your configuration data and server inventory stay on your hardware the whole time.

Sources: PHP advisory GHSA-w8hw-w9fx-j79w, PHP advisory GHSA-6wmf-4rv5-p3rp, Datadog Security Labs, BleepingComputer, The Record, The Hacker News

The same PHP version can be an emergency on one host and a routine patch on the next

CVEasy AI reads CISA KEV, vendor advisories, and researcher write-ups continuously, then TRIS scores each finding against your actual inventory on your own hardware.

Related Reading