TL;DR
- PHP2Shell is a set of memory-safety bugs in the PHP engine itself, not in applications built on PHP. They are tracked as two CVEs, CVE-2026-109618 and CVE-2026-109619, each rated CVSS v4.0 9.2 (Critical) 234.
- Confirmed impact: unauthenticated remote code execution as the web worker user, plus remote disclosure of process memory 34.
- Fixed in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35, released 2026-10-21. PHP says updating is the only complete fix 2.
- Exploitation attempts have been reported, the earliest on 17 October, four days before disclosure 978. No confirmed compromises have been published: CISA said it had not observed exploitation as of its 21 October advisory, and neither CVE was in its Known Exploited Vulnerabilities catalog as of 22 October 1019.
- Exposure is enormous: Censys counts 6.3 million hosts and 115 million web properties running PHP (any version) 12, and Palo Alto Networks counts over 22.6 million exposed instances that are potentially vulnerable 13.
Overview
This write-up is based mainly on Wiz Research's technical post, checked against PHP's own advisories and the other sources listed at the end. It explains how the bugs work. It deliberately leaves out exploitation parameters, payloads and request details.
On 2026-10-21 the PHP project shipped security releases for two critical vulnerabilities and published GitHub advisories GHSA-w8hw-w9fx-j79w ("RCE via OOM corruption", CVE-2026-109618) and GHSA-6wmf-4rv5-p3rp ("RCE via stale global state", CVE-2026-109619) 234. The name "PHP2Shell" comes from the researchers; php.net does not use it 2.
Both advisories give the vector 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: network-reachable, no privileges or user interaction needed, but high attack complexity and attack requirements present 34.
Discovery was joint. Calif describes the bugs as "discovered by Calif, OpenAI and Wiz" 5, and Wiz says it worked with the PHP team, Calif and OpenAI 1. Cloudflare also names Quarkslab among the disclosing parties 8. The advisories credit reporters lcfr-hax, sagitzadik-wiz, tomash-areion and amyb-oai 34.
Background: fatal errors and reused workers
When PHP hits a fatal error, especially out-of-memory (OOM) on exceeding memory_limit, it historically used longjmp() to leave the current context. Native code could be interrupted part-way through updating its state, before request memory was fully initialized or before globals pointing into it were reset 35.
Under persistent SAPIs such as Apache mod_php and PHP-FPM, the worker process survives the fatal error and serves later requests. Per-process globals (the BG, PS, EG and CG families) can therefore keep pointers into a request heap that has already been freed. A later request on the same worker may use that stale state before initialization replaces it, which is a use-after-free 14.
Calif places this in a long history of PHP memory-limit problems. It cites a 2004 memory_limit vulnerability by Stefan Esser, PHP bug #78876 and a 2024 OSTIF/Quarkslab audit finding 5.
The bugs
Wiz describes four bug classes 1. The PHP advisories describe the two CVEs as root-cause classes rather than individual bugs, and the sources do not agree on how all four bugs map to the CVEs (see the note at the end of this section).
1. Upload-progress unserializer use-after-free
During multipart (RFC1867) parsing, Session upload-progress handling stores request-owned decoder bookkeeping in the BG(unserialize) global. An allocation failure can destroy the request heap while that state stays published. A later request on the same worker then treats attacker-reclaimed memory as live bookkeeping 1. Wiz and Sansec map this bug to CVE-2026-109618 16.
2. Remote process-memory disclosure via PHPSESSID
Upload-progress handling can initialize Session state early and publish a session ID in PS(id). If an OOM aborts the request, PS(id) can be left pointing to freed memory. A later multipart request on the same worker reaches cleanup before Session initialization resets the pointer. PHP then URL-encodes the reclaimed heap contents into a PHPSESSID response cookie. This works even if the application never calls session_start(), and the leaked memory can include other requests' passwords, tokens, API keys and certificates 16.
3. Fatal-error backtrace use-after-free
EG(last_fatal_error_backtrace) holds the most recent fatal error's backtrace and is not cleared when an OOM destroys the heap. A diagnostic emitted early in the next request, before the executor reinitializes, can touch the dangling backtrace 1. PHP's advisory describes this as a PHP 8.5 path with fatal_error_backtraces enabled, where the reset "is always missed during shutdown", and calls it the strongest demonstrated case 4. Mapped to CVE-2026-109619 146.
4. OPcache shared-memory type confusion
In zend_accel_script_persist(), PHP advances the process-local CG(map_ptr_last) frontier before publishing the shared one. An allocation failure between those two steps lets another worker reuse a slot for a different object type, which corrupts shared OPcache state across workers 1.
CVE mapping. Wiz and Sansec both put the upload-progress use-after-free and the memory disclosure under CVE-2026-109618, and the backtrace use-after-free and OPcache confusion under CVE-2026-109619 16. The split is not clean, though: PHP's CVE-2026-109619 advisory lists memory disclosure among its confirmed impacts and names session upload progress as one of its confirmed cases 4. Wiz adds that more variants exist and that the patch fixes the root issue across them 1.
How the official fixes work
For CVE-2026-109618, the patch removes longjmp() from OOM handling. A soft OOM is now deferred to the next VM safe point, and a hard OOM above a higher threshold exits via _exit() 3. For CVE-2026-109619, the patch resets a wider set of request-owned global state 4.
How the bugs combine
Exploitation follows a producer/consumer pattern. A producer request leaves request-owned state behind after a fatal error. A consumer request on the same worker then uses it before initialization 4. Wiz reports these chains 1:
- Unserializer UAF plus memory disclosure (which removes address uncertainty): unauthenticated RCE on WordPress, Drupal and Joomla.
- Backtrace UAF: unauthenticated RCE on Drupal.
- OPcache type confusion: unauthenticated RCE on WordPress.
- Memory disclosure alone: leaks secrets without depending on the application.
Calif claims pre-auth RCE against WordPress 7.1.2, Drupal 11.4.3, Joomla 6.1.4, MediaWiki 1.46.2, Roundcube 1.7.4, Magento 2.4.9, phpMyAdmin 5.2.3, Nextcloud 35.0.1, Laravel 13.11, Symfony 8.1 and WHMCS 5. These demonstrations are self-reported and have not been independently verified.
Impact and affected versions
Versions. The official advisories list affected versions as 8.2.x before 8.2.35, 8.3.x before 8.3.36, 8.4.x before 8.4.27 and 8.5.x before 8.5.12 34. Wiz, Calif, Sansec and Cloudflare say the vulnerable code dates back to PHP 5.4.0 1568, and Calif notes that some paths apply only to specific versions or SAPIs 5. PHP 8.1 and older receive no official releases. PHP published minimal source backports for 7.4, 8.0 and 8.1 as php-src branches named PHP-x.y-CVE-2026-109618-109619 2. PHP 8.2 itself reaches end of life on 31 December 2026 2.
SAPIs. PHP says every installation serving web requests is at risk: PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed. It rates risk highest for mod_php 2. For CVE-2026-109618, RCE is confirmed only on mod_php, and FPM, FrankenPHP and LSAPI susceptibility rests on source analysis 3. For CVE-2026-109619, the session upload-progress path was validated on both mod_php and PHP-FPM 4.
Vendor ratings differ. PHP rates both CVEs Critical (CVSS v4.0 9.2) 34. Red Hat rates them Important: RHEL runs PHP-FPM by default, FPM workers exit on early startup failures before reaching the corrupted structures, and reliable exploitation needs precise heap manipulation and timing. It considers default mod_php setups at higher risk 14.
Reach. Wiz's headline figure of "70% of the Internet" is its own framing. Cloudflare cites W3Techs for PHP running "almost 70%" of websites 18. Censys counts 6,292,667 hosts and 115,000,393 web properties running PHP of any version 12, and Palo Alto Networks counts over 22.6 million exposed instances potentially vulnerable, from its Cortex Xpanse telemetry 13. Both measure exposure, not confirmed vulnerability. Reliability depends on the PHP build, extensions, allocator layout and whether the producer and consumer requests land on the same worker. Running multiple workers lowers reliability but does not remove the risk 34.
Timeline
Wiz's and Calif's timelines record different discovery dates because each describes its own work 15. Both are shown below.
| Date (2026) | Event | Source |
|---|---|---|
| Aug 1 | Calif begins finding RCE paths | 5 |
| Aug 11 | Calif reports its first bug to PHP | 5 |
| Aug 14-15 | Calif shares an RCE PoC; PHP confirms it works | 5 |
| Aug 19 | Wiz finds its first bug | 1 |
| Aug 20-21 | Wiz confirms unauthenticated RCE on Drupal, then WordPress; Calif reports a second bug (Aug 21) | 15 |
| Aug 24 | Wiz reports to the PHP security team | 1 |
| Sep 21 | PHP acknowledges Wiz's report and begins a coordinated fix (per Wiz) | 1 |
| Oct 17 | Amazon's MadPot honeypots record a PHP2Shell exploit attempt, four days before disclosure. Amazon says it is unclear how the actor got the details | 9 |
| Oct 20 | The CVE IDs leak, carrying an incorrect CVSS score of 10.0. PHP pre-announces the release; disclosure, originally planned for Oct 22, moves forward to Oct 21 at 14:00 UTC | 21116 |
| Oct 21, 03:02 UTC | Cloudflare WAF protection enabled by default | 8 |
| Oct 21, 07:34–07:43 UTC | Hosting partners report exploitation incidents to Patchstack; Cloudflare reports early attacks, hours before the fix ships | 176 |
| Oct 21, 14:00 UTC | Fixed releases and public disclosure | 26 |
| Oct 21 | watchTowr observes exploitation attempts against both CVEs; Patchstack telemetry shows only probing, with no hard evidence of breaches; CISA says it has not observed exploitation | 71710 |
| Oct 22 | Amazon Linux, SUSE (LTSS), CloudLinux and Synology ship fixed packages. A SimpleXML crash regression in 8.4.27 and 8.5.12 is fixed upstream, not yet released. Neither CVE is in CISA's KEV catalog | 24212219 |
Detection
Application logs may show little, because the bugs sit below the application layer 1. Watch for 1:
- unusual multipart requests followed by memory-limit errors;
- repeated HTTP 500 responses;
- unexpectedly large or malformed
PHPSESSIDresponse cookies; - unexplained PHP worker churn.
PHP recommends an isolated audit with E_ALL logging, looking for repeated early warnings, cleanup-time diagnostics, fatal errors and timeouts. Clean logs do not prove safety, because some failures happen before PHP writes a message 4. For incident response, preserve logs, configuration and suspicious files before making changes, then hunt for web shells, unexpected cron jobs and new accounts 7. Start the investigation window at 17 October, the earliest known exploit attempt, not at the disclosure date 9.
Read-only checks you can run now
Adjust paths to your distribution. All of these only read logs and process lists.
# Out-of-memory fatal errors (the trigger condition) in Apache and PHP-FPM logs
sudo grep -h "Allowed memory size of" /var/log/apache2/error.log* /var/log/php*-fpm.log 2>/dev/null | tail -n 50
# Workers crashing: Apache children or FPM workers dying on signals
sudo grep -hE "exit signal (Segmentation fault|Aborted)" /var/log/apache2/error.log* 2>/dev/null | tail -n 20
sudo grep -hE "exited on signal" /var/log/php*-fpm.log 2>/dev/null | tail -n 20
# Bursts of HTTP 500 on POST requests, by client IP (combined log format)
sudo awk '$6 == "\"POST" && $9 == 500 {print $1}' /var/log/apache2/*access.log | sort | uniq -c | sort -rn | head
# Shells or download tools running as the web server user
ps -eo user,pid,ppid,etime,cmd --forest | awk '$1 == "www-data"' | grep -Ev "apache2|php-fpm|php-cgi"
# PHP files changed in web roots since the first known exploit attempt (possible web shells)
sudo find /var/www -name "*.php" -newermt "2026-10-17" -ls 2>/dev/null | head -n 50Oversized PHPSESSID response cookies usually don't appear in default access logs. Look for them in reverse-proxy, load-balancer or WAF logs that record response headers.
Mitigation
Patch. Upgrade to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or to your distribution's patched package, then restart every PHP worker. Commands for each platform are in our patch guide. PHP says: "There are no complete mitigations, updating is the only safe choice." 27 Updating application code or plugins does not fix these bugs 8. Users of rpms.remirepo.net did not have updates on release day 2; Remi's fixed builds were in the repository by 22 October 18.
After patching. Restart long-running PHP processes as well as the web server: queue workers and other CLI daemons keep running the old PHP until restarted (see the patch guide). Many vendors backport the fix without changing the version number, so check their changelog for the CVE IDs rather than trusting php -v 21. The 8.4.27 and 8.5.12 releases can crash with zend_mm_heap corrupted in SimpleXML code; that regression is a crash, not a security hole, and is fixed upstream pending the next release 22. Don't roll back to a vulnerable version. A separate unserialize() use-after-free still open in php-src is unrelated to PHP2Shell 23.
Partial interim measures, which reduce exposure to specific paths but do not make an installation safe 2:
- Set
memory_limit=-1administratively (php_admin_value), making sure nothing restores a finite limit. Monitor memory afterwards, because a single request can then use all of it 26. - Do not use RLIMIT_AS or RLIMIT_DATA, because they can trigger the vulnerable path. Use cgroup or container memory limits with care 23.
- Keep
post_max_sizelow and enforce request size, part count and header limits at the frontend. Block multipart/form-data there if the application does not use it 23. - Set
session.upload_progress.enabled=0in php.ini or the pool or server config (not viaini_set()or.user.ini) and restart workers 2. - On PHP 8.5, set
fatal_error_backtraces=0and restart workers 24. - Remove per-request INI settings that raise diagnostics before initialization, and consider
pm.max_requests=1(at a significant performance cost) 24. - WAF rules are defense in depth only. Cloudflare's have been on by default for every plan with its WAF, including Free, since 03:02 UTC on 21 October 8. On AWS WAF, use
AWSManagedRulesPHPRuleSet2.3 or later and the core rule set'sSizeRestrictions_BODYrule 20. CloudLinux says no Imunify360 rule blocks the out-of-memory path of CVE-2026-109618 21. Full list in the patch guide.
Lessons
- Error paths are attack surface. A fatal error that skips cleanup in a long-lived worker becomes a cross-request use-after-free 4.
- Engine-level bugs bypass application hardening and application maintenance modes, because PHP parses the request before any application code runs 6.
- AI-assisted auditing played a central role: Wiz credits Wiz Atlas with Gemini 4 Argon and GPT-5.6 Sol, and Calif describes LLM-driven variant analysis 15. Calif says its initial discovery used no special harness and only generally available models 5. Claims about speed and effort are the vendors' own.
- Exploitation can start before disclosure. Amazon saw an attempt on 17 October, four days before the fix, and Cloudflare reported attacks hours before release 96.
Sources
- Wiz Research: PHP2Shell: AI uncovers critical RCE vulnerabilities affecting 70% of the Internet (vendor research; product sections are marketing)
- PHP.net: 2026 news archive (security release announcements)
- GHSA-w8hw-w9fx-j79w: RCE via OOM corruption (CVE-2026-109618)
- GHSA-6wmf-4rv5-p3rp: RCE via stale global state (CVE-2026-109619)
- Calif: php2shell
- Sansec: PHP2Shell: Critical PHP RCE
- watchTowr Intel: PHP Remote Code Execution (PHP2Shell)
- Cloudflare: Cloudflare deploys protections against php2shell
- AWS Security Blog: Amazon Threat Intelligence identifies PHP2Shell exploitation prior to public disclosure in MadPot
- NBC News: Critical vulnerabilities in commonly used web code spark rush to update systems (quotes CISA's 21 October advisory)
- Tenable: php2shell FAQ (quotes the PHP Foundation)
- Censys: Oct 21 Advisory: PHP Unauthenticated Remote Code Execution
- Unit 42 (Palo Alto Networks): Threat Brief: Critical Vulnerabilities in PHP
- Red Hat: RHSB-2026-013, PHP request handling memory corruption
- CERT-EU: Security Advisory 2026-018, Critical Vulnerabilities in PHP
- Ubuntu: php2shell (CVE-2026-109618 and CVE-2026-109619) fixes available
- Patchstack: Major security alert: PHP vulnerable to critical RCE
- Remi's RPM repository: php-common package history
- CISA: Known Exploited Vulnerabilities Catalog (checked 22 October, catalog version 2026.10.21)
- AWS Security Blog: Using AWS WAF to apply mitigations for PHP2Shell RCE vulnerabilities
- CloudLinux: PHP CVE-2026-109618 and CVE-2026-109619 (fixed packages, verification and Imunify360 rules)
- php-src #24488: 8.4.27 / 8.5.12 regression, SimpleXML properties table double free
- php-src #24481: use-after-free in var_destroy() during unserialize() re-entrancy
- Amazon Linux Security Center: CVE-2026-109618