
PHP has released 8.5.12, 8.4.27, 8.3.36 and 8.2.35, fixing two critical remote code execution vulnerabilities in PHP itself — not in a plugin, not in a framework. Both are CVSS v4.0 9.2. Researchers call the pair PHP2Shell: send a request, get a shell. Complete RCE has been validated over HTTP against Apache mod_php. PHP’s own words: there are no complete mitigations, updating is the only safe choice.
What PHP2Shell is
- Two critical flaws in the PHP core, both rated CVSS v4.0 9.2 by the PHP security team.
- Impact reported by researchers: remote code execution, unauthenticated. The nickname implies exactly that: one request in, a shell out.
- The name was coined by researchers and the press. PHP itself does not use it.
- The name comes from a leaked screenshot and banner of an exploit tool called PHP2Shell, credited to Calif.IO, which claims to work against every PHP release from 5.4.0 onward.
- The embargo was meant to lift on Thursday 22 October at 11:00 ET. It broke a day early.
Status as of 14:55 UTC on 21 October
| Item | Status |
|---|---|
| Official advisory | Published — php.net security advisory at 13:48 UTC, signed by the PHP security team (php.net news) |
| CVE records (MITRE) | PUBLISHED: CVE-2026-109618 “RCE via OOM corruption” at 13:48:34 UTC, CVE-2026-109619 “RCE via stale global state” at 13:54:43 UTC |
| GitHub advisories | GHSA-w8hw-w9fx-j79w (109618) and GHSA-6wmf-4rv5-p3rp (109619), both critical, CVSS v4.0 9.2, credits 6-hacker team |
| NVD | Still no record (totalResults 0 at 14:50 UTC) |
| EPSS | No score yet |
| CISA KEV | Not listed |
| Affected versions | Confirmed: PHP 8.2.* < 8.2.35, 8.3.* < 8.3.36, 8.4.* < 8.4.27, 8.5.* < 8.5.12. The “everything since 5.4” claim is false |
| Fixed versions | 8.5.12, 8.4.27, 8.3.36 and 8.2.35 are out; minimal source backports for 7.4, 8.0, 8.1 on separate branches |
| Active exploitation | Cloudflare reported early attacks at 07:43 UTC; WAF rules live for all plans. watchTowr and Huntress had seen none in their own telemetry |
| Public exploit | No official PoC. PHP confirms complete RCE was validated over HTTP against mod_php with default limits; PoCs in circulation hardcode build-specific offsets |
Root cause: what PHP confirms
Until 14:00 UTC this section was built on leaked analysis. It no longer has to be: the two advisories are public and they describe two different bugs, not two variants of one.
CVE-2026-109618 — “RCE via OOM corruption” (GHSA-w8hw-w9fx-j79w)
PHP uses longjmp() to bail out of the current context on out-of-memory errors. Some contexts leave request memory temporarily reachable before it is initialised; when an allocation fails there, PHP exits early and leaves that memory invalid. The failed allocation does not even have to be related to the invalid memory. Request shutdown then reads or branches on it, and repeated OOM strategies reliably turn that into remote code execution. Weaknesses: CWE-824 (uninitialised pointer), CWE-416 (use-after-free), CWE-755, CWE-1325.
The part that matters operationally: three multipart parsing problems let a request reach OOM while staying inside its own PHP limits — a multipart body with no usable declared content length slips past the post_max_size check, a single part can carry an unrestricted number of headers whose in-memory representation is far larger than the wire form, and empty file controls do not count towards max_file_uploads but still allocate the file metadata arrays. Nested field names multiply that work.
Per SAPI: mod_php is worst — it runs request shutdown even when request startup failed, before any application code runs, and complete RCE was validated there over HTTP with default limits. FPM exits the child instead, which removes the child from the pool and prevents consumption of the invalid state; source analysis still calls FPM susceptible, but no complete FPM exploit has been demonstrated. FrankenPHP behaves like mod_php and is likely susceptible without application help; LSAPI models FPM.
CVE-2026-109619 — “RCE via stale global state” (GHSA-6wmf-4rv5-p3rp)
PHP keeps error, session, object and extension state in worker globals that point into memory owned by a single request. Normally that state is reset before the request heap is freed. It isn’t always: a fatal error abandons an operation before its matching reset, and a warning or other non-fatal diagnostic can repopulate error state after shutdown already cleared it. The next request then consumes a dangling pointer. Exploitation needs a producer (an operation that publishes request-owned state into a global) and a consumer (a later operation that reads, frees or branches on it) — and both must land on the same worker.
Confirmed impact: unauthenticated heap use-after-free, memory disclosure and RCE with the web worker’s privileges. The fatal-backtrace path was validated against mod_php; the session-lifecycle path was validated against both mod_php and FPM. Other state families only count once a concrete producer/consumer pair is shown — which is why the advisory is deliberately narrower than the panic.
Both advisories credit the same people. Reporters: Ryan at Calif.io, Wiz (sagitzadik, tomash-areion) and OpenAI (amyb-oai) — an AI lab is named as a co-reporter, which is new for a PHP advisory. Remediation developers: Arnaud Le Blanc, Ilija Tovilo, Jakub Zelenka and Nora Dossche. That explains the “PHP2Shell” branding: the exploit is the researcher’s, the fix is PHP’s.
Timeline
- 20 Oct, morning — a Hacker News post (since removed) claims two PHP RCEs, CVSS 10.0, disclosure on Thursday 22 Oct. Source: “trust me, bro”.
- 20 Oct — screenshots of an exploit tool named PHP2Shell (Calif.IO) spread on social media.
- 20 Oct — watchTowr confirms the leak is real: RCE, embargo broken (“we were all supposed to wait until Thursday 11am ET”). No technical detail shared.
- 20 Oct 19:33 UTC — PHP publishes advance notice on php.net: security release Wed 21 Oct 14:00 UTC, two criticals, both CVSS 4.0 9.2. PHP reserves the right to release earlier if details go public or exploitation appears.
- 21 Oct 02:42 UTC — Huntress rapid response. No PoC of its own, no exploitation in partner environments, advice on process detection (Florian Roth’s
process-watcher). - 21 Oct 07:43 UTC — Cloudflare reports early attacks; WAF rules live for all customers including the free plan (CTO Dane Knecht, who calls them a stopgap).
- 21 Oct — the Netherlands scales down as a precaution: Tweakers, the House of Representatives, NCSC, Security.NL, DigiD and RTL go offline, alongside Japanese services such as pixiv and several e-commerce platforms.
- 21 Oct 09:00–12:00 UTC — a wave of analyses (Sansec, Aikido, The Hacker News, SecurityAffairs, Phoenix Security, SOC Prime, ThreatWire, Modat, PatchMon), all with the same gap: no affected or fixed versions.
- 21 Oct 13:48 UTC — CVE-2026-109618 is published by MITRE with the PHP project as CNA: title “RCE via OOM corruption”, affected ranges 8.2/8.3/8.4/8.5, CVSS v4.0 9.2, GitHub advisory GHSA-w8hw-w9fx-j79w. php.net publishes the security advisory the same minute: “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.” No complete mitigations exist — updating is the only safe choice.
- 21 Oct 13:54 UTC — CVE-2026-109619 is published: “RCE via stale global state”, same four affected ranges, same 9.2, GHSA-6wmf-4rv5-p3rp.
- 21 Oct ~14:00 UTC — the four releases land: 8.5.12, 8.4.27, 8.3.36, 8.2.35. php.net immediately serves the new versions on its release endpoints. Each release note repeats that there are no complete mitigations. PHP 8.2 reaches end of life on 31 December 2026, so this is one of its last security releases.
- 21 Oct ~14:00 UTC — the scope collapses. The affected ranges are only 8.2–8.5; the leaked claim that every PHP from 5.4.0 is at risk does not survive contact with the advisory. For EOL branches PHP publishes minimal source backports for 7.4, 8.0 and 8.1 (commits from 18 Oct, message “Minimal mitigation for EOL version”), for people who build from source or have no LTS vendor — there are no official releases for those branches.
- 21 Oct ~14:00 UTC — php.net also names mod_php as the highest risk and confirms “a demonstrated exploit where an unauthenticated request can trigger the attack before any application code runs”. FrankenPHP “likely susceptible”, LSAPI “models FPM”.
- 21 Oct ~14:00 UTC — the docs say explicitly
rpms.remirepo.netwill not have updates today; switch to distribution packages or build from source. Distributions and LTS vendors did have the patches in advance. - 21 Oct 14:50 UTC — NVD still has no record for either CVE (totalResults 0), EPSS has no score and neither appears in CISA KEV. Vulnerability databases will lag the vendor for hours.
- 21 Oct 15:00 UTC — the ChangeLog for 8.5.12 lists exactly two security fixes: GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp, “Avoid state corruption via bailout”.
Exposure in numbers
- Modat (scan 20 Oct): 4.38 million PHP services on 1.89 million hosts; PHP runs on roughly 47% of the top 15 million sites.
- ~884,000 internet-facing hosts advertise PHP 8.1 or older (Shodan, 20 Oct) — about 59% of hosts with a readable version. Those branches get no official release: PHP shipped minimal source backports for 7.4, 8.0 and 8.1 instead, so the fix has to be built or taken from an LTS vendor. Another ~740,000 hosts report PHP without a version.
- Modat: about 79% of services with a known version run an end-of-life branch.
- Sansec expects mass exploitation within 15 minutes of the release.
Mitigations: what PHP itself says now
Updating is the only complete fix, and PHP says so twice. The advisory does publish partial measures that reduce exposure to specific attack paths. They do not make an installation safe, and PHP explicitly says so — but if a fix cannot go out within the hour, this is the list, not the blogs’ version of it.
CVE-2026-109618 — RCE via OOM corruption
- Set
memory_limit=-1, in a place applications cannot override:php_admin_valuein the FPM pool or Apache configuration. Then verify no pool, directory or application setting puts a finite limit back. This removes the controllable memory-limit failure; a genuine allocation failure can still reach the vulnerable path. - Set a low
post_max_size: 8M if the application does not need uploads, 32M if it does. Keepupload_max_filesizebelowpost_max_size. - Enforce limits at the web server or proxy for decoded request size, input field count, multipart part count, header count and nesting depth — and make sure those limits also cover requests without a usable Content-Length, because that is one of the three OOM strategies.
- Use a container or cgroup limit as an availability boundary, with a policy that kills only the affected worker. Do not substitute
RLIMIT_ASorRLIMIT_DATA: those cause exactly the allocation failure the bug feeds on.
CVE-2026-109619 — RCE via stale global state
; php.ini, an FPM pool, or server config — must apply BEFORE request parsing
session.upload_progress.enabled = 0
; skip on PHP 8.5; blocks the confirmed fatal-backtrace path only
fatal_error_backtraces = 0
- Restart every worker after changing it. Do not rely on
ini_set()or FPM.user.ini: those come too late. This blocks the confirmed session paths, not other variants. - Remove invalid or deprecated per-request settings that emit diagnostics before initialisation. Hiding the diagnostics with
error_reporting,display_errorsor@does not help — the stale state is already published. - Fix attacker-reachable fatal errors and execution timeouts, including failures in callbacks, upload or import processing and cleanup. Every abandoned operation is a potential producer.
A ready-made PHP-FPM pool drop-in for both holes, in the order the advisory lists them:
; must load BEFORE request parsing: a pool file, not .user.ini or ini_set()
[www]
; CVE-2026-109618 — remove the controllable OOM path
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 32M
php_admin_value[upload_max_filesize] = 24M
; CVE-2026-109619 — block the confirmed session producer
php_admin_flag[session.upload_progress.enabled] = off
php_admin_flag[session.use_strict_mode] = on
; PHP 8.5 only, same drop-in:
; php_admin_flag[fatal_error_backtraces] = off
Then restart every worker (reload is not enough for php_admin_value changes on some SAPIs) and verify the values in a request served by PHP-FPM, not from the CLI. Two things this does not do: it does not stop a natural allocation failure on a process that genuinely runs out of memory, and it does not cover producers other than the session path. An empty limit also removes PHP’s own safety net, so the container or cgroup limit has to take over that job — with a kill policy aimed at the single worker, and without RLIMIT_AS or RLIMIT_DATA.
What earlier drafts recommended — blocking multipart/form-data at the web server, read-only mode, Cloudflare — remains useful as defence in depth, but the advisory reframes it: the OOM bug does not need multipart, only a request that exhausts memory, and the stale-state bug does not need uploads at all. There is no configuration that closes either hole.
Patching and detection
- The fix ships for 8.2, 8.3, 8.4 and 8.5 only. PHP 8.1 and older are end of life and get no vendor fix; CloudLinux may backport. In every case you patch the PHP runtime on the server, never the application code.
- Detection: alert on
php-fpm,php-cgi,httpdorapache2spawning shells, interpreters or download tools; on reverse-shell command lines; and onwhoami,id,uname -aoripconfigrun straight from the web server. - Florian Roth’s process-watcher logs every program a PHP process starts, with its command line. It detects; it prevents nothing.
- SigmaHQ’s web-server rule does not cover php-fpm, php-cgi or FrankenPHP. On nginx + php-fpm the process that spawns commands is
php-fpm, so you need a rule on that. With auditd, logexecveandexecveatforeuid=33(www-data on Debian/Ubuntu) for both b32 and b64. - On a hit: isolate the host, preserve web server and PHP logs, then hunt for web shells, new files in the web root, new cron jobs or scheduled tasks, and new accounts.
What was wrong in the coverage
With the advisories public, the scorecard can be settled. Most of the panic-age claims did not survive.
- “CVSS 10.0” — wrong. Both are 9.2, exploit complexity high, attack requirements present. Serious, not a 10.
- “Every PHP since 5.4 is vulnerable” — false. Affected ranges are 8.2–8.5 only. The leak extrapolated from a tool banner.
- “PHP named the CVEs” — actually true after the fact: the PHP project is the CNA. Before 13:48 UTC the identifiers came from the leak.
- “No RCE has been demonstrated” — overturned. PHP confirms complete RCE over HTTP against mod_php with default limits and normal prefork configurations.
- “It is a multipart/session bug” — half right. The OOM bug is reachable through multipart parsing but is fundamentally a
longjmp()state bug; the stale-state bug is a worker-global use-after-free that needs no upload at all. - “Only mod_php is exposed” — wrong in the other direction. Mod_php is worst and is where RCE was validated; the session path of 109619 was validated against FPM too.
- “PHP2Shell” — still a researcher/press label, and now traceable: the reporter credited first is “Ryan at Calif.io”, the vendor from the leaked banner.
- php.net — news archive 2026 (advance notice)
- GHSA-w8hw-w9fx-j79w — CVE-2026-109618, RCE via OOM corruption (full technical advisory)
- GHSA-6wmf-4rv5-p3rp — CVE-2026-109619, RCE via stale global state (full technical advisory)
- php.net — ChangeLog 8.5.12
- MITRE cve-id API — CVE-2026-109618 (RESERVED)
- NVD — CVE-2026-109618 (no record)
- FIRST EPSS — CVE-2026-109618 (empty)
- Sansec — critical PHP RCE, mitigations, timeline, Dutch impact
- Huntress (John Hammond) — rapid response
- The Hacker News — PHP2Shell: two unpatched critical PHP RCE flaws
- Third-party root-cause analysis (unconfirmed)
- Hacker News discussion — 432 comments of hands-on PoC reports
- PenTestIT — detection engineering: SigmaHQ gaps, auditd
- Modat — internet exposure report
- Aikido — critical vulnerabilities in PHP
- SecurityAffairs — PHP prepares emergency fixes
- Ars Technica — prepare for critical security patches
- ThreatWire — what is confirmed about the two CVSS 9.2 flaws
- Phoenix Security — PHP core RCE
- Wikipedia — PHP2Shell
- Tweakers — PHP waarschuwt voor twee ernstige bugs
Sources
Primary
Technical and incident response
Exposure and overviews
Updated 21 October 2026, 15:00 UTC: the advisory, both CVE records and the four releases are now public. Confirmed facts, both GHSA advisories and PHP’s own mitigation list have been folded in; NVD and EPSS had not caught up yet at the time of writing.
