# PHP2Shell: AI uncovers critical RCE vulnerabilities affecting 70% of the Internet

Cross-request use-after-free and type-confusion bugs in PHP's fatal-error, upload-progress and OPcache paths enable unauthenticated RCE and worker memory disclosure.

- Payout: Research
- Severity: critical
- Program: PHP
- Credited: Wiz Research
- Disclosed: 2026-10-21
- CVE: CVE-2026-109618, CVE-2026-109619
- Write-up quality: 9/10
- Original: https://www.wiz.io/blog/php2shell-php-rce
- Library page: https://bugbountylibrary.com/r/blog%3Awiz-io-blog-php2shell-php-rce
- Tags: domain:oss impact:data-leak impact:rce kind:research platform:threat-intel sev:critical tech:php technique:chained technique:source-review vuln:info-disclosure vuln:memory-corruption vuln:rce

**Target:** PHP core engine (session upload-progress, fatal-error backtrace, OPcache)
**Root cause:** Fatal out-of-memory errors destroy the request heap without clearing executor/session globals (BG(unserialize), PS(id), EG(last_fatal_error_backtrace)) or the OPcache shared frontier, leaving dangling pointers and stale shared metadata that a later request on the same worker reuses.
**Steps:**
1. Send a multipart RFC1867 request that triggers session upload-progress unserialization, then force an out-of-memory fatal error to abort before cleanup, leaving BG(unserialize)/PS(id) pointing at freed request heap.
2. Send a second multipart request to the same worker; upload-progress cleanup path runs before Session init resets the pointer, so PHP treats attacker-reclaimed heap as a live zend_string and URL-encodes it into the `PHPSESSID` response cookie (memory disclosure, works without session_start()).
3. Use the disclosure to defeat address uncertainty, then reclaim the freed unserializer bookkeeping with controlled data to corrupt memory and hijack execution.
4. Variant: trigger fatal OOM so EG(last_fatal_error_backtrace) dangles, then emit an early diagnostic on the next request to follow attacker-reclaimed memory.
5. Variant: cause allocation failure inside zend_accel_script_persist() after CG(map_ptr_last) advances but before the shared frontier publishes, then have another worker reuse the slot for a different runtime object, producing cross-worker type confusion.
6. Run the combined php2shell script against wordpress:latest, drupal:latest, joomla:latest to get unauthenticated code execution.
**How found:** Wiz Research used frontier AI models (Wiz Atlas, Gemini 4 Argon, GPT-5.6 Sol) to audit PHP's error-handling paths, hypothesizing that fatal OOM errors leave corrupted global state reused across requests; AI iterated exploit variants and adapted them to differing memory layouts.
**Exploitation:** Unauthenticated attacker sends crafted multipart requests to a PHP worker; an induced OOM leaves stale global state, a follow-up request leaks worker memory via the PHPSESSID cookie or corrupts memory, and chaining disclosure with the UAF/type-confusion primitives yields remote code execution on WordPress, Drupal and Joomla without any application-level flaw.
**Escalation:** Chained memory disclosure with the use-after-free to remove address uncertainty, then adapted the exploit per target's memory layout (PHP version/config) to reach RCE across multiple CMS deployments.
**Insight:** Fatal-error and OOM abort paths that skip cleanup of executor/session globals are a cross-request attack surface: look for globals published before module init and never undefined on abnormal termination, then pair a leak primitive (e.g. data reflected into a response header/cookie) with a stale-pointer reuse on the same long-lived worker.
