Vulnerabilities

10 min readUpdated

PHP2Shell: CVE-2026-109618 and CVE-2026-109619 explained

Quick answer

PHP2Shell is the common name for two critical PHP vulnerabilities disclosed on 21 October 2026. CVE-2026-109618 is memory corruption triggered when PHP aborts a request on an out-of-memory error; CVE-2026-109619 is a use-after-free from internal state that outlives a request on a reused worker. Both can give an unauthenticated attacker remote code execution. They are fixed in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Upgrading is the only complete fix.

At a glance

critical severity
Identifier
CVE-2026-109618
Also known as
PHP2Shell
Product
PHP (php-src), all SAPIs serving web requests
Affected
PHP 8.2 before 8.2.35; PHP 8.3 before 8.3.36; PHP 8.4 before 8.4.27; PHP 8.5 before 8.5.12; Older branches back to 7.3 (CVE-2026-109618) and 5.4 (CVE-2026-109619); no vendor release, source backports for 7.4/8.0/8.1
Fixed in
PHP 8.5.12; PHP 8.4.27; PHP 8.3.36; PHP 8.2.35
CVSS
9.2 (CVSS 4.0, PHP); 9.8 (CVSS 3.1, NVD). CVE-2026-109619: 9.2 / 8.1
Disclosed
Known exploited (CISA KEV)
Not listed

Patched 21 October 2026. Exploitation attempts seen from 17 October 2026, before disclosure; public proofs of concept from 21 October. Not in CISA KEV as of 24 October 2026.

01What is PHP2Shell (CVE-2026-109618 and CVE-2026-109619)?

PHP2Shell is the nickname for two related memory-safety flaws in the core PHP engine, disclosed together on 21 October 2026. Both can let an unauthenticated attacker run code on a server, or read the memory of a PHP worker, without any valid account. They are not bugs in WordPress, Drupal or any single application: the weakness is in PHP itself, so a site is exposed even when its own code is sound.

CVE-2026-109618, "RCE via OOM corruption", happens because PHP uses a long jump to abort the current request when a memory allocation fails. In some code paths that jump leaves request memory only partly initialised, and the request cleanup step then reads or branches on that invalid memory. CVE-2026-109619, "RCE via stale global state", is a use-after-free: worker-global state can outlive the request memory it points to, and a later request on the same worker can act on it.

The PHP security team rates both at CVSS 4.0 9.2 (critical); the US National Vulnerability Database scores CVE-2026-109618 at 9.8 and CVE-2026-109619 at 8.1 under CVSS 3.1. For a sense of scale, PHP2Shell sits alongside Log4Shell and React2Shell as a core-platform bug that reaches far beyond any one application. An unpatched PHP runtime is exactly the out-of-date component risk that the OWASP Top 10 now files under software supply chain failures (A03:2025).

02Who is affected, and which setups are most exposed?

Every PHP installation that serves web requests before the fixed builds is affected, including end-of-life branches. The OOM issue (CVE-2026-109618) traces back to PHP 7.3; the stale-state issue (CVE-2026-109619) traces back as far as 5.4. In practice, treat any public server running a PHP build from before 21 October 2026 as vulnerable until its package is updated.

How you run PHP changes how easy it is to reach. The server API (SAPI) matters because the confirmed, application-independent path fires during request start-up, before any application code runs. Apache with mod_php is the highest-risk configuration for exactly that reason. PHP-FPM is harder to exploit but still susceptible.

SAPIExposureWhy
Apache mod_phpHighestRuns request shutdown even after a failed start-up, so the attack lands before any application code
PHP-FPMSusceptibleChildren exit on start-up failure, but a validated path sets state during application code and reuses it on a later request
FrankenPHPLikely susceptibleSource analysis shows it calls request shutdown unconditionally, like mod_php
LiteSpeed (LSAPI)ConditionalModels FPM; application or extension behaviour decides reachability
php-cgiAt riskNamed by the PHP advisory among the setups at risk; no separate exploit detail published

03How do the two bugs work?

Both are memory-safety faults rooted in how PHP handles fatal errors mid-request. CVE-2026-109618 is reached when a request runs out of memory while internal structures are half-built. Multipart form parsing is one documented way to drive a request to exhaust its memory limit while still passing the usual size checks, which is why limiting multipart bodies at the edge is part of the mitigation. When the out-of-memory abort fires at the wrong moment, cleanup treats invalid data as valid, and that is the corruption primitive.

CVE-2026-109619 needs a producer and a consumer in separate requests on the same worker. A producing operation stores request-owned state in a worker-global location; a fatal error, including an out-of-memory error, abandons the normal reset; PHP frees the request heap while the global still points into it; and a later request reads that stale state before initialisation replaces it. The result is a cross-request use-after-free. Confirmed cases involve fatal-error backtrace state on PHP 8.5 and session upload-progress handling.

Chained together and tuned to a target's memory layout, these primitives were demonstrated as unauthenticated remote code execution, and also as plain memory disclosure that can leak passwords, tokens and keys belonging to other requests. No exploit code or step-by-step method is reproduced here. The Wiz researchers, who found the bugs with AI-assisted analysis, note that language models also make it easier to adapt such an exploit to different PHP versions and memory layouts.

04How do I check whether I am affected?

Check two things: the build that actually serves web requests, and the SAPI it runs under. The upstream version number alone can mislead on Linux distributions, because they backport the fix under their own package version while keeping the headline PHP version. Note too that php -v reports the command-line build, which can differ from the FPM or Apache module build on a server with several PHP versions installed.

Confirm the PHP build and SAPI (Debian/Ubuntu names; adjust the version)
php -v                     # the CLI build only; always prints (cli)
php-fpm8.3 -v              # the FPM build, if you run FPM
apache2ctl -M 2>/dev/null | grep -i php   # a php module listed = mod_php

# Debian / Ubuntu: the real package version, including the distro suffix
dpkg -l | grep -E '^ii.*php[0-9]'

# RHEL / Alma / Rocky
rpm -qa 'php*'

# Does the installed changelog name the fix? (a miss is not proof either way)
zgrep -m1 CVE-2026-109618 /usr/share/doc/php*/changelog.Debian.gz

05Fixed versions per branch and distro backports

Upgrade to the patched release for your branch and restart every PHP worker so no process keeps running the old code. One upgrade closes both CVEs. The fix removes the long jump for out-of-memory errors and resets the wider worker state that could survive a request boundary.

PHP 8.1 and older receive no release from the PHP project. The team published minimal source backports for 7.4, 8.0 and 8.1 for people who build from source, but the supported path is to move to a current branch or rely on a distribution that backports the fix to its package.

  • 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)
  • Ubuntu 22.04 jammy: php8.1 8.1.2-1ubuntu2.28; 24.04 noble: php8.3 8.3.6-0ubuntu0.24.04.13; 26.04 resolute: php8.5 8.5.4-0ubuntu1.5 (all USN-8948-1)
  • Ubuntu 14.04 to 20.04 (ESM, Ubuntu Pro): for example php7.4 7.4.3-4ubuntu2.29+esm5 on 20.04 (USN-8948-2)
  • Red Hat Enterprise Linux 10: php 8.3.36-1.el10_2 (RHSA-2026:87070) and php8.4 8.4.27-1.el10_2 (RHSA-2026:87071); RHEL 8 and 9 receive fixed php module streams under related advisories, so check the stream you have enabled
  • Match the package version your vendor publishes, then restart the service; the nginx 1.18.0 triage page shows the same backport logic
BranchFixed buildNote
PHP 8.58.5.12Supported
PHP 8.48.4.27Supported
PHP 8.38.3.36Supported
PHP 8.28.2.35Security support ends 31 December 2026
PHP 8.1 and olderNo upstream releaseEnd of life; source backports exist for 7.4, 8.0, 8.1
PHP 8.6 (pre-release)8.6.0 RC4Test builds only; earlier 8.6 previews are affected

06If you cannot patch immediately

These are the PHP project's own stopgaps. They break specific links in each chain but do not replace the upgrade. Apply them in start-up configuration (php.ini, an FPM pool, or the web-server config), never through per-request overrides, and restart every worker afterwards.

  • Do not substitute an operating-system RLIMIT_AS or RLIMIT_DATA for the PHP memory limit: those make the underlying allocation fail and can reach the vulnerable path
  • Enforce limits at the proxy or web server for decoded request size, field count, multipart part count, header count and nesting depth, including for requests with no usable Content-Length
  • Use a container or cgroup memory limit as an availability boundary, ideally with a policy that kills only the affected worker; it is not a complete mitigation
  • Where performance allows, the advisory also suggests pm.max_requests = 1 for FPM: each worker then serves a single request, so no later request can land on stale state left in it
  • Remove invalid or deprecated INI settings applied per request; hiding diagnostics with error_reporting or display_errors does not help
  • Fix attacker-reachable fatal errors and execution timeouts in upload, import, callback and cleanup paths
Stopgap settings from the PHP advisory, in FPM pool syntax (illustrative; not executed here)
; FPM pool file: php_admin_* values cannot be changed by the application
; CVE-2026-109618: no finite, controllable memory limit
php_admin_value[memory_limit] = -1
; CVE-2026-109619: close the confirmed session upload-progress path
php_admin_flag[session.upload_progress.enabled] = off
; CVE-2026-109619, PHP 8.5 only: close the fatal-error backtrace path
php_admin_flag[fatal_error_backtraces] = off

; php.ini: 8M if the application takes no uploads, 32M if it does
post_max_size = 8M
; keep upload_max_filesize below post_max_size
upload_max_filesize = 2M

; Apache mod_php: the same values in the server or vhost config, e.g.
;   php_admin_value memory_limit -1
;   php_admin_flag session.upload_progress.enabled off

07What can an edge WAF block, and what only the upgrade fixes?

An edge layer can see and stop the demonstrated trigger for CVE-2026-109618: multipart bodies with no usable length, parts carrying huge numbers of headers, or deeply nested field names, aimed at a PHP endpoint. Enforcing a decoded body-size limit, a multipart part and header count, and a nesting-depth cap at a separate, patched tier directly removes the easiest way to drive a worker out of memory, which is why several CDN and cloud providers published stopgap WAF rules for the known vectors around disclosure day. A web application firewall used this way is virtual patching: it narrows the window, it does not close the hole.

What an edge filter cannot do is fully fix either CVE. The out-of-memory condition behind CVE-2026-109618 can also be reached by ordinary application allocations that look nothing like an upload. CVE-2026-109619 is a cross-request fault: its demonstrated session path starts with a multipart upload, so the same limits narrow it, but other producers and consumers are application-specific, and no request pattern covers them all. The bug executes inside the PHP worker, so a filter in front of an unpatched origin reduces exposure, as any reverse proxy does by shielding the origin and absorbing flood and scan traffic, but the upgrade is the fix. Pairing request limits at the edge with rate limiting slows mass scanning; it does not make the PHP engine safe.

MeasureHelps CVE-2026-109618?Helps CVE-2026-109619?Complete fix?
Upgrade PHP to the fixed buildYesYesYes
Edge limits on multipart size/parts/depthYes, narrows the main vectorPartly (the upload-progress path)No
memory_limit = -1 (admin-enforced)PartlyNoNo
session.upload_progress.enabled = 0NoBlocks a confirmed pathNo
Blocking individual scanner IPsNoNoNo

08How do I lock the origin to the proxy while I patch?

Edge limits only help traffic that actually passes through the edge. If an attacker learns the origin's real IP address, they can send multipart requests straight to PHP and skip every rule the proxy enforces. So while you patch, make sure the origin is not directly reachable: keep its address off public DNS and old records, and firewall it to accept web traffic only from the proxy's published ranges. The full method is in how to hide your origin IP.

An origin-side allow list is the simplest enforcement. The nginx and nftables snippets below use documentation ranges; substitute the ranges your proxy provider gives you. If the origin restores visitor addresses with nginx's real_ip module, its allow rules see the visitor's address rather than the proxy's, so enforce the lock in the firewall instead.

Origin allow list (nginx server block, then an nftables ruleset)
# nginx: accept only the proxy's ranges on this vhost
server {
    listen 443 ssl;
    ssl_certificate     /etc/ssl/certs/origin.pem;
    ssl_certificate_key /etc/ssl/private/origin.key;
    allow 192.0.2.0/24;      # replace with your proxy's published ranges
    allow 2001:db8::/32;
    deny  all;
    client_max_body_size 8m;
}

# nftables: drop web traffic that does not come from the proxy (IPv4 and IPv6)
table inet originlock {
    set proxy4 { type ipv4_addr; flags interval; elements = { 192.0.2.0/24, 198.51.100.0/24 } }
    set proxy6 { type ipv6_addr; flags interval; elements = { 2001:db8::/32 } }
    chain input {
        type filter hook input priority filter; policy accept;
        tcp dport { 80, 443 } ip saddr @proxy4 accept
        tcp dport { 80, 443 } ip6 saddr @proxy6 accept
        tcp dport { 80, 443 } drop
    }
}

09Timeline

DateEvent
19 Aug 2026Wiz Research discovers the first PHP2Shell bug with AI-assisted analysis; PHP later also credits Calif and OpenAI, among others
20-21 Aug 2026Unauthenticated RCE confirmed against Drupal, then WordPress, in a lab
24 Aug 2026Wiz reports to the PHP security team
21 Sep 2026PHP acknowledges the reports and starts a coordinated fix
17 Oct 2026Earliest exploitation attempt seen by Amazon's MadPot honeypots, four days before disclosure
20 Oct 2026The CVE identifiers circulate on social media; PHP announces a security release for 21 October at 14:00 UTC, a day earlier than planned
21 Oct 2026, from 02:00 UTCProof-of-concept exploits and further honeypot attempts appear before the embargo lifts
21 Oct 2026, 14:00 UTCPHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 released with the advisories; more proofs of concept follow within hours
24 Oct 2026Not in the CISA KEV catalogue (catalogue version 2026.10.23)

Questions

Frequently asked questions

Two. PHP2Shell is the common name for CVE-2026-109618 (memory corruption from out-of-memory handling) and CVE-2026-109619 (a use-after-free from stale worker state). They were disclosed together on 21 October 2026, both rated CVSS 4.0 9.2, and one PHP upgrade fixes both. They are independent bugs with different triggers, so neither mitigation on its own covers the other.

PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35, released on 21 October 2026. Linux distributions ship the fix under their own package numbers, such as Debian bookworm php8.2 8.2.35-1~deb12u1 and Ubuntu jammy php8.1 8.1.2-1ubuntu2.28. PHP 8.1 and older get no official release, though minimal source backports exist for 7.4, 8.0 and 8.1. Restart every worker after upgrading.

No. An edge WAF can block the known multipart upload vector for CVE-2026-109618 and enforce request-size and nesting limits, which buys time, and several providers published such rules. But the out-of-memory condition can be reached other ways, and CVE-2026-109619 has application-specific paths that no request pattern covers. Only upgrading PHP closes both. Treat edge rules as virtual patching, not a fix.

Exploitation attempts, yes. Amazon's honeypot network logged a first attempt on 17 October 2026, four days before disclosure, then proofs of concept and more attempts from the early hours of 21 October, before the 14:00 UTC release. As of 24 October 2026 the CVEs are not in the CISA Known Exploited Vulnerabilities catalogue, which is no reason to wait.

No. mod_php is the highest-risk setup because the attack can land before application code runs, but PHP-FPM is still susceptible: a confirmed path publishes state during application execution and consumes it on a later request to the same worker. FrankenPHP and LiteSpeed are also in scope. The SAPI changes the difficulty, not whether you need to patch.

Sources

Sources and verification

nftables origin-lock ruleset checked with nft 1.0.6 (nft -c, then loaded and listed) in an isolated network namespace; the nginx allow-list and body-limit server block with nginx -t (nginx 1.22.1) using a scratch certificate; on Debian 12, October 2026. The PHP and FPM settings are taken from the PHP security advisory and the php, php-fpm, apache2ctl and rpm checks were not executed (no PHP on the test machine). Package versions from the Debian, Ubuntu and Red Hat trackers, read on 24 October 2026.

  1. 01NVD: CVE-2026-109618 · NVD
  2. 02NVD: CVE-2026-109619 · NVD
  3. 03PHP security advisory: critical RCE vulnerabilities, update now · php.net
  4. 04GHSA-w8hw-w9fx-j79w: RCE via OOM corruption (CVE-2026-109618) · PHP GitHub
  5. 05GHSA-6wmf-4rv5-p3rp: RCE via stale global state (CVE-2026-109619) · PHP GitHub
  6. 06PHP 8 ChangeLog (8.5.12, 8.4.27, 8.3.36, 8.2.35) · php.net
  7. 07Debian security tracker: CVE-2026-109618 · Debian
  8. 08Ubuntu security notice USN-8948-1 (PHP vulnerabilities) · Ubuntu
  9. 09Red Hat: CVE-2026-109618 · Red Hat
  10. 10PHP2Shell: how AI helped find and fix the vulnerabilities · Wiz Research
  11. 11Amazon Threat Intelligence: PHP2Shell exploitation before public disclosure · AWS
  12. 12CISA Known Exploited Vulnerabilities catalogue · CISA
  13. 13Ubuntu security notice USN-8948-2 (PHP vulnerabilities, ESM releases) · Ubuntu
  14. 14php2shell (CVE-2026-109618, CVE-2026-109619): frequently asked questions · Tenable
  15. 15PHP supported versions · php.net
  16. 16OWASP Top 10:2025, A03 Software Supply Chain Failures · OWASP

Last updated