On October 21, 2026, the PHP Foundation shipped an emergency release that quietly rewrote one of the foundational assumptions of web security. Two critical vulnerabilities, grouped under CVE-2026-109618 and CVE-2026-109619 and nicknamed PHP2Shell, were disclosed by Wiz Research with support from Calif, OpenAI, and the PHP security team. The headline number is hard to overstate: PHP powers roughly 70% of all websites on the internet, so a flaw in the engine itself, rather than in any single application, lands on an enormous share of the live web at once.
What makes PHP2Shell different from a typical CMS plugin bug is where it lives. These are memory-safety defects inside the core PHP runtime, reachable before authentication, and in some cases before the target application code ever executes a line. A "Hello World" script on a vulnerable interpreter is exposed just the same as a sprawling WordPress install. In this breakdown, the Redfox Cybersecurity team walks through what the bugs actually are, why AI was the catalyst that turned decade-old theory into working exploits, and the concrete detection, hunting, and remediation steps your security team should run today.
PHP2Shell is not a single bug. It is a family of memory-corruption primitives that share a common root cause: PHP mishandles fatal errors during request processing, most importantly out-of-memory (OOM) conditions. When a request dies unexpectedly, the engine can tear down the request heap while leaving stale pointers and global bookkeeping behind. A later request handled by the same long-lived worker process can then reuse that corrupted state, which opens the door to either reading leftover process memory or corrupting it to gain code execution.
Wiz grouped four demonstrated bugs under two CVEs:
Both CVEs reached a CVSS severity of 9.2. Every PHP release from the last decade is affected by at least one issue, going back as far as PHP 5.4. The fixed branches are PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35. If you are running anything older, assume you are in scope. If you are not sure what your estate is running, a focused exposure assessment from Redfox Cybersecurity is the fastest way to get an authoritative answer.
The reason the blast radius is so large is architectural. PHP typically runs as a pool of persistent worker processes under PHP-FPM, mod_php, or a FastCGI handler. Those workers survive across many HTTP requests for performance. That persistence is exactly what the attacker abuses: a corrupted global left behind by one request is still resident when the next request arrives at the same worker. Because the vulnerable code paths sit in the Session module, the multipart (RFC1867) upload parser, and OPcache, they are reachable by an unauthenticated client sending ordinary-looking HTTP traffic.
Commentators have compared PHP2Shell to Heartbleed and Log4Shell, and the analogy is apt. Like Heartbleed, one variant leaks adjacent process memory that may contain passwords, session tokens, API keys, and TLS material. Like Log4Shell, the flaw is buried in a ubiquitous dependency that most organizations cannot fully inventory. The difference in 2026 is the discovery mechanism, which we will come back to shortly.
To defend against PHP2Shell intelligently, you need a working mental model of each primitive. None of what follows is a weaponized exploit, and Redfox Cybersecurity does not publish functioning memory-corruption chains for an internet-scale, pre-auth RCE. The goal here is to give defenders enough understanding to build accurate detections and prioritize patching.
PHP can decode session data during upload-progress handling in an early pre-initialization window, storing request-owned bookkeeping in the internal BG(unserialize) state. If an allocation failure interrupts that path, the request heap is destroyed but the unserializer's global state is left published. A subsequent request handled by the same worker can then consume attacker-reclaimed memory as if it were live unserializer bookkeeping, producing a powerful corruption primitive. Combined with the memory-disclosure bug to defeat address uncertainty, Wiz turned this into unauthenticated RCE.
This is the quietest and arguably scariest primitive. PHP parses multipart requests during request startup, before normal module initialization completes. Upload-progress handling can initialize Session state at that early stage and publish a session ID in the internal PS(id) pointer. An OOM error can abort before cleanup finishes, leaving PS(id) pointing at freed memory. A later multipart request to the same worker reaches the cleanup path first, and PHP trusts the stale pointer as a live string, URL-encoding reclaimed heap contents straight into the PHPSESSID response cookie. Critically, this works even if the application never calls session_start(), which is why it is application-agnostic. The attacker simply reads an oversized session cookie full of someone else's memory.
Affected versions retain the backtrace of the most recent fatal error in the executor global EG(last_fatal_error_backtrace). An OOM fatal error can destroy the request heap without clearing that value, so the worker survives holding a pointer into the previous request's memory. If the next request emits a diagnostic early enough, PHP follows the dangling backtrace into attacker-reclaimed memory, yielding a cross-request use-after-free that can be shaped into controlled corruption. Wiz demonstrated unauthenticated RCE against Drupal with this path.
OPcache stores compiled PHP structures in shared memory and tracks indices through a shared frontier. During script persistence, PHP advances a process-local frontier but only publishes the shared frontier after persistence completes. An allocation failure between those two steps leaves partially persisted objects referencing slots that shared state still thinks are free. When another worker reuses one of those slots, stale metadata resolves to a pointer of the wrong type, creating a cross-worker type confusion that corrupts shared OPcache state. This became the RCE chain against WordPress.
The common thread is clear. Fix the fatal-error cleanup logic and you kill the whole class, which is exactly what the PHP patch does.
The vulnerable code had been sitting in the PHP codebase for more than ten years. It survived a decade of manual audits not because nobody could see the bugs in principle, but because weaponizing cross-request heap corruption against a persistent worker pool is brutally hard. Exploits depend on precise memory layout, and that layout shifts with PHP version, build flags, extension load order, and application behavior. An exploit that lands on one deployment can fail completely on the next, and each adaptation demands fresh analysis, testing, and debugging.
Wiz credits frontier AI models, specifically Wiz Atlas, Gemini 4 Argon, and GPT-5.6 Sol, with collapsing that effort. The models helped locate the flaws, adapt exploits across memory layouts, analyze failed attempts, and iterate automatically. Work that would have taken a world-class team months was compressed into roughly a week for a single researcher. That is the real inflection point: complexity is no longer a reliable moat. Vulnerabilities that were "technically exploitable but practically ignored" are now firmly in scope for both researchers and adversaries.
The defensive lesson is uncomfortable but actionable. If AI can mass-adapt an exploit to thousands of slightly different targets, your patch window is shorter than you think, and your detection has to be behavioral rather than signature-locked to one payload. This is precisely the kind of adversary-emulation and detection-engineering work the Redfox Cybersecurity team builds into red team and purple team engagements.
Start with the boring, decisive question: what PHP version is actually serving traffic. Do not trust an asset inventory you have not validated against the live edge.
The fastest external check is the response headers and known PHP behaviors. Many hardened servers suppress X-Powered-By, so lean on multiple signals.
# Pull server-side language hints from HTTP headers
curl -sI https://target.example.com/ | grep -iE 'x-powered-by|server|set-cookie'
# PHP often sets a PHPSESSID cookie; capture it to confirm PHP is in the stack
curl -sI https://target.example.com/ | grep -i 'PHPSESSID'
# Probe a known PHP entrypoint and watch for PHP-specific error signatures
curl -s -o /dev/null -w "%{http_code}\n" https://target.example.com/index.php
[cta]
On hosts you own and can authenticate to, confirm the exact build directly rather than inferring it.
# Local version check across common deployment styles
php -v
php-fpm -v 2>/dev/null || /usr/sbin/php-fpm8.3 -v
# Enumerate every PHP binary on the box (handy on multi-version servers)
for b in $(compgen -c | grep -E '^php[0-9.]*$' | sort -u); do
printf "%s -> " "$b"; "$b" -v 2>/dev/null | head -n1
done
[cta]
For large estates, map exposure at scale with Censys or Shodan queries, then reconcile against your CMDB. These queries surface internet-facing PHP that is outside patched version ranges.
# Censys search for PHP-powered hosts advertising a version banner
services.http.response.headers.x_powered_by: "PHP*"
# Shodan: find hosts exposing older PHP via the Server/X-Powered-By banner
http.component:"PHP" port:443,80
# Narrow to pre-patch branches you care about
http.headers.x_powered_by:"PHP/8.1" OR http.headers.x_powered_by:"PHP/7"
[cta]
Nuclei is the right tool for repeatable, fleet-wide version triage. The template below flags any host advertising a PHP version outside the patched branches. Treat it as a prioritization aid, not proof of exploitability, since banners can be spoofed or stripped.
id: php2shell-version-triage
info:
name: PHP2Shell Affected Version Triage (CVE-2026-109618/109619)
author: redfox-cybersecurity
severity: high
description: Flags PHP banners outside patched branches 8.5.12/8.4.27/8.3.36/8.2.35.
tags: php,php2shell,cve-2026-109618,cve-2026-109619
http:
- method: GET
path:
- "{{BaseURL}}/"
matchers-condition: and
matchers:
- type: regex
part: header
regex:
- "(?i)X-Powered-By:\\s*PHP/([0-9]+\\.[0-9]+\\.[0-9]+)"
- type: dsl
dsl:
- "!contains(tolower(header), 'php/8.5.12')"
- "!contains(tolower(header), 'php/8.4.27')"
- "!contains(tolower(header), 'php/8.3.36')"
- "!contains(tolower(header), 'php/8.2.35')"
[cta]
Run it across an authorized scope and feed the output into your ticketing pipeline so remediation is tracked, not just discovered.
nuclei -t php2shell-version-triage.yaml -list authorized_targets.txt \
-rl 50 -timeout 10 -o php2shell_affected.txt
[cta]
Version triage tells you where you are exposed. Detection tells you whether someone is already knocking. Because these bugs execute below the application layer, your WordPress or Drupal logs may show almost nothing useful. Hunt at the web server, FPM, and network layers instead.
The disclosure primitive leaks heap bytes into the PHPSESSID response cookie. Normal session identifiers are short and consistent. Abnormally large or malformed Set-Cookie: PHPSESSID= values, especially following multipart requests, are a strong signal. The script below is a benign monitoring harness that flags oversized session cookies. It observes and alerts only; it does not extract or weaponize leaked data.
#!/usr/bin/env python3
# Passive detector: flags abnormally large PHPSESSID response cookies
import sys, re
NORMAL_MAX = 64 # typical PHPSESSID length is well under this
cookie_re = re.compile(r"Set-Cookie:\s*PHPSESSID=([^;\r\n]+)", re.IGNORECASE)
for line in sys.stdin: # pipe tcpdump/zeek/proxy logs here
m = cookie_re.search(line)
if not m:
continue
value = m.group(1)
if len(value) > NORMAL_MAX or re.search(r"%[0-9A-Fa-f]{2}", value):
print(f"[ALERT] Oversized/encoded PHPSESSID ({len(value)} bytes): {line.strip()[:160]}")
[cta]
Pair this with multipart-request anomaly detection. The exploit paths are reached through RFC1867 multipart uploads that trigger memory-limit errors, so correlate unusual multipart POSTs with spikes in HTTP 500s and worker restarts.
# Correlate multipart uploads with memory-limit fatals in PHP-FPM logs
grep -iE 'multipart/form-data' /var/log/nginx/access.log \
| awk '{print $1}' | sort | uniq -c | sort -rn | head
grep -iE 'Allowed memory size|out of memory|Fatal error' /var/log/php*-fpm.log \
| tail -n 50
[cta]
Encode the indicators as a Sigma rule so it ports across your SIEM. The behavioral tell is repeated multipart requests followed closely by memory exhaustion and 500 responses from the same source.
title: PHP2Shell Exploitation Indicators
id: 7c2f9a10-php2-shell-4d1b-ioc-redfox
status: experimental
description: Detects multipart requests correlated with PHP OOM fatals and worker churn.
logsource:
category: webserver
detection:
multipart_post:
cc-content-type|contains: 'multipart/form-data'
cs-method: 'POST'
server_error:
sc-status: 500
oom_fatal:
c-message|contains:
- 'Allowed memory size'
- 'out of memory'
timeframe: 2m
condition: multipart_post and (server_error or oom_fatal)
level: high
tags:
- attack.initial_access
- cve.2026.109618
- cve.2026.109619
[cta]
If you cannot patch immediately, a web application firewall buys time. Several providers, including Cloudflare and Fastly, deployed managed rules around disclosure. For self-managed edges, a ModSecurity rule that inspects for the abnormal response-cookie pattern and rate-limits suspicious multipart bursts is a reasonable stopgap. Virtual patching is a bridge, not a cure, and Redfox Cybersecurity recommends treating it as a holdover until the engine itself is updated.
# ModSecurity: flag suspiciously large PHPSESSID in responses (virtual patch)
SecRule RESPONSE_HEADERS:Set-Cookie "@rx (?i)PHPSESSID=[^;]{128,}" \
"id:92026618,phase:3,deny,status:403,log,\
msg:'Possible PHP2Shell memory disclosure: oversized PHPSESSID',\
tag:'CVE-2026-109618'"
# Throttle rapid multipart POST bursts from a single source
SecRule REQUEST_HEADERS:Content-Type "@rx multipart/form-data" \
"id:92026619,phase:1,pass,nolog,\
setvar:'ip.mp_counter=+1',expirevar:'ip.mp_counter=60'"
SecRule IP:mp_counter "@gt 40" \
"id:92026620,phase:1,deny,status:429,log,\
msg:'Multipart flood consistent with PHP2Shell probing',tag:'CVE-2026-109619'"
[cta]
Wiz was explicit that these four bugs are instances of a broader pattern, and that other variants likely exist. If you ship PHP extensions, custom engine builds, or C code that interacts with PHP's request lifecycle, hunt for the same anti-pattern: globals or pointers that survive a fatal error without being reset during cleanup. Semgrep and CodeQL are the professional-grade choices here.
# Semgrep: look for executor/session globals referenced after fatal handling
semgrep --lang c \
-e 'EG(last_fatal_error_backtrace)' \
-e 'PS(id)' \
-e 'BG(unserialize)' \
./php-src/
# CodeQL: build a database and run a use-after-free style query pack
codeql database create php-db --language=cpp --command="make -j4"
codeql database analyze php-db \
codeql/cpp-queries:Security/CWE/CWE-416 \
--format=sarifv2.1.0 --output=php2shell-variants.sarif
[cta]
Reviewing the SARIF output for cross-request pointer reuse is tedious work, and it is exactly the kind of deep code review where an experienced application security partner earns its keep. If your team needs a second set of expert eyes on a PHP engine fork or a high-risk extension, the Redfox Cybersecurity application security practice can help.
Patching the interpreter is the only complete fix. Everything else is mitigation. Move to a patched branch and then restart workers so no stale, vulnerable process keeps serving traffic.
# Debian/Ubuntu: update PHP and restart the worker pool
sudo apt update && sudo apt install --only-upgrade php8.3 php8.3-fpm
php -v # confirm 8.3.36 or newer
sudo systemctl restart php8.3-fpm
sudo systemctl restart nginx
# RHEL/Alma/Rocky via Remi or vendor stream
sudo dnf upgrade php php-fpm
sudo systemctl restart php-fpm httpd
# Containers: rebuild on a patched base image, do not patch in place
# FROM php:8.3.36-fpm (or 8.4.27 / 8.5.12 / 8.2.35)
docker build --pull -t myapp:patched .
docker rollout restart deployment/myapp # or your orchestrator's equivalent
[cta]
While you roll patches, reduce attack surface with defense-in-depth hardening. Disabling upload-progress tracking where it is not needed removes one of the primary trigger paths, and tightening session cookie handling limits what a disclosure bug can expose.
; php.ini hardening to shrink the PHP2Shell trigger surface
session.upload_progress.enabled = Off
session.use_strict_mode = On
session.cookie_httponly = On
session.cookie_secure = On
; Keep memory pressure predictable so OOM states are rarer and easier to spot
memory_limit = 256M
max_input_vars = 1000
file_uploads = On
upload_max_filesize = 8M
[cta]
After patching, verify the fix held across every pool and every version on multi-tenant hosts, then re-run your Nuclei triage to confirm nothing slipped through. For regulated environments, capture the before-and-after evidence so you can demonstrate remediation during your next audit. A structured post-incident validation, which Redfox Cybersecurity delivers as part of incident response and continuous testing, turns a scramble into a repeatable process.
PHP2Shell is a preview of the next several years of vulnerability management. The bugs were old; the exploits were new, and AI is what closed the gap. Three shifts deserve a place in your roadmap.
First, patch velocity now has to assume adversaries are automating exploit adaptation. The comfortable gap between disclosure and reliable mass exploitation is shrinking, so your mean time to patch for internet-facing dependencies needs to be measured in hours and days, not weeks.
Second, detection must be behavioral. Signature rules tied to one payload will not survive an attacker who can regenerate variants on demand. Hunt for the underlying mechanics, such as the memory-disclosure cookie anomaly and the multipart-plus-OOM correlation shown above, rather than a specific byte sequence.
Third, your dependency inventory is your exposure map. Organizations that could answer "where does PHP 8.1 run and who owns it" within minutes contained PHP2Shell quickly. Those that could not are still finding forgotten servers. If you want an authoritative, tested view of your PHP footprint and how an attacker would chain through it, the offensive security team at Redfox Cybersecurity can run that assessment for you.
PHP2Shell, tracked as CVE-2026-109618 and CVE-2026-109619 at CVSS 9.2, is a core-engine flaw affecting every PHP release since 5.4 and roughly 70% of all websites. The root cause is improper cleanup after fatal out-of-memory errors, which leaves stale state for later requests to abuse into memory disclosure or unauthenticated remote code execution. Patched branches are PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35, and nothing short of upgrading the interpreter fully resolves it.
The practical response is straightforward even if the bug is not. Confirm your real PHP versions at the live edge, map exposure with Nuclei, Censys, and Shodan, deploy behavioral detections for the disclosure and multipart-OOM signatures, apply WAF virtual patches as a bridge, then upgrade and restart every worker. The deeper lesson is that AI has permanently changed the cost of exploitation, and your security program should be stress-tested against that reality. When you are ready to validate your defenses against AI-accelerated attacks, Redfox Cybersecurity is here to help at https://www.redfoxsec.com/.