PHP2Shell (CVE-2026-109618) Explained: Architecture & Patch Guide
How the two-request PHP2Shell RCE exploit (CVE-2026-109618 & CVE-2026-109619) hijacks web workers pre-login, and how to patch and lock down your servers.
Author: Logic42 Sovereign Engineering Practice
PHP2Shell (CVE-2026-109618 and CVE-2026-109619) is a pair of critical, unauthenticated Remote Code Execution (RCE) vulnerabilities (CVSS 9.2) in the core PHP runtime uncovered during AI-assisted code audits. The flaws let an attacker trigger an out-of-memory abort on Request #1 and hijack leftover memory pointers on Request #2 to execute shell commands before application login code runs.
Because PHP powers roughly 75% of websites with a known server-side language—spanning content platforms, e-commerce checkouts, and internal banking portals—the disclosure triggered immediate worldwide advisories from CISA, Rapid7, and Palo Alto Networks. The vulnerabilities surfaced when frontier AI reasoning models (including Anthropic's Claude Mythos working alongside Alpha-Omega and the PHP Foundation) audited decade-old C memory-handling routines inside the core PHP engine.
Most security advisories bury the remediation under dense C pointer arithmetic. Below, we break down how the two-request exploit works in practice, how to verify and patch active servers in five minutes, and how to lock down legacy PHP fleets that can't be upgraded overnight.
Why Passwords and Login Screens Don't Stop PHP2Shell
When a critical web vulnerability breaks, the first question most teams ask is: "We enforce strong passwords, Two-Factor Authentication (2FA), and application firewalls. Aren't we protected?"
Not against this flaw. PHP2Shell triggers inside the underlying PHP engine at the front door, before your application router, login screen, or security middleware even initializes.
To visualize how the exploit bypasses authentication, think of a PHP web worker (PHP-FPM or mod_php) as an Express Bank Teller Booth:

- The Normal Rule (Wipe the Desk Between Visitors): Spawning a brand-new process for every web visitor is too slow. Instead, your web server keeps the same Worker Process sitting at a desk serving Visitor #1, Visitor #2, and Visitor #3 back-to-back. After each request finishes, the worker shreds the temporary request data and wipes its memory workspace clean before taking the next connection.
- Request #1 (
CVE-2026-109618: The Memory Overload Abort): The attacker arrives as Visitor #1 and submits a malformed payload designed to trip the server's Out-of-Memory (OOM) limit during request setup. An emergency abort (longjmp()) fires immediately. Because the worker jumps straight out of its normal execution path, it skips a portion of the desk-wiping sequence, leaving an uninitialized "Override Slip" (a dangling memory structure) sitting on the counter. - Request #2 (
CVE-2026-109619: Hijacking the Uncleaned Worker): Milliseconds later, the attacker sends Visitor #2 to that exact same worker booth. Before the application even asks for a session cookie or password, the worker reads the leftover Override Slip from Visitor #1 (Use-After-Free) and executes the attacker's command (ROOT SHELL). - The Remediation (Patch the Engine, Flush RAM Workers & Lock the OS):
You upgrade PHP to the patched release (so the worker always sanitizes memory during an abort), restart the worker pool (so workers still sitting at dirty desks in RAM are replaced), and apply a kernel syscall lock (
systemd/eBPF) so a web worker can't ever spawn a command shell.
Quick Verification Checklist for Engineering & Hosting Teams
If an external vendor, cloud team, or managed hosting provider operates your PHP servers, send them these three verification questions today:
Urgent verification check for PHP2Shell (CVE-2026-109618 & CVE-2026-109619):
1. Are all PHP runtimes and containers upgraded to 8.5.12+, 8.4.27+, 8.3.36+, or 8.2.35+?
2. Have php-fpm and apache2/httpd worker pools been restarted so no pre-patch binaries remain in RAM?
3. For any legacy PHP 8.1 or older workloads, have we disabled session.upload_progress.enabled and restricted shell execution?
The 5-Minute Verification & Patch Guide
If you have terminal access to your infrastructure, follow this five-step triage sequence to audit and patch your nodes:
| PHP Release Branch | Vulnerable Versions | Patched Safe Version | Official Upstream Status | Action Required Today |
|---|---|---|---|---|
| PHP 8.5 | 8.5.0 to 8.5.11 | 8.5.12 or later | Active Support | Upgrade binary + restart php-fpm / apache2 |
| PHP 8.4 | 8.4.0 to 8.4.26 | 8.4.27 or later | Active Support | Upgrade binary + restart php-fpm / apache2 |
| PHP 8.3 | 8.3.0 to 8.3.35 | 8.3.36 or later | Security Support | Upgrade binary + restart php-fpm / apache2 |
| PHP 8.2 | 8.2.0 to 8.2.34 | 8.2.35 or later | Security Support | Upgrade binary + restart php-fpm / apache2 |
PHP <= 8.1 (7.4, 8.0, 8.1) | All versions (5.4 to 8.1) | No official binary patch | End-of-Life (EOL) | Apply WAF + systemd Kernel Containment immediately |
Step 1: Audit Running PHP Versions Across Host and Containers
Check both the CLI binary and the active php-fpm daemon, as multi-version hosts frequently bind web pools to an older minor release:
# Check CLI and FPM versions on the host OS
php -v
php-fpm -v || /usr/sbin/php-fpm8.3 -v
# Audit all running Docker containers with PHP installed:
docker ps --format "{{.Names}}" | xargs -I {} sh -c 'echo "=== {} ==="; docker exec {} php -v 2>/dev/null'
Step 2: Upgrade to the Patched Runtime Binary
# Ubuntu / Debian (including Ondrej PPA):
sudo apt-get update && sudo apt-get install --only-upgrade -y 'php*'
# RHEL / AlmaLinux / Rocky Linux / Amazon Linux:
sudo dnf upgrade -y 'php*'
Step 3: Flush Vulnerable Worker Pools From RAM
Upgrading packages via apt or dnf replaces the binary on disk, but it doesn't replace php-fpm or apache2 worker processes that are already resident in memory. Until you restart the daemon, your server remains exploitable:
# Restart PHP-FPM (adjust version for 8.2, 8.3, 8.4, or 8.5)
sudo systemctl restart php8.3-fpm
# Restart Apache mod_php or reload Nginx upstream sockets:
sudo systemctl restart apache2 || sudo systemctl restart httpd
sudo systemctl reload nginx
# Confirm the master process start time reflects the restart:
ps -eo pid,lstart,cmd | grep -E "php-fpm|apache2|httpd" | grep -v grep
SAPI Memory Forensics & Zero-Trust Containment for Legacy Fleets
In enterprise environments, the biggest operational risk is End-of-Life (EOL) version drift. Across financial services, telecommunications, and public-sector estates, internal billing portals and vendor appliances frequently remain pinned to PHP 7.4, 8.0, or 8.1 because upgrading to PHP 8.3 breaks legacy dependencies.
When upstream binary patches aren't available for an EOL branch, you must break the exploit chain at the reverse proxy, the PHP SAPI configuration, and the Linux kernel syscall boundary.
How CVE-2026-109618 and CVE-2026-109619 Chain Inside zend_mm_heap
CVE-2026-109618(Uninitialized Heap State via OOMlongjmp()): When a PHP worker exceedsmemory_limitduring early request parsing, the Zend Engine invokes_zend_bailout(), executing a Clongjmp()back to the SAPI loop. In unpatched builds, specific allocation paths fail to zero-initialize internal structures before the allocation call that triggers the OOM jump. The subsequent cleanup handler reads uninitialized heap bytes that an attacker can groom across prior requests.CVE-2026-109619(Global State Use-After-Free inext/session/session.c): Persistent SAPI workers (PHP-FPM,mod_php,FrankenPHP,LiteSpeed) maintain module globals across requests. During abnormal resets (specifically involvingPHP_SESSION_UPLOAD_PROGRESSstate tracking inext/session/session.c), global pointers referencing request-allocated memory (emalloc) are freed during shutdown without being nulled or properly re-initialized on the next cycle. When Request #2 lands on that same worker PID, it dereferences the freed pointer, allowing craftedzend_functionstructures to divert control flow straight intozend_call_function("system")beforeindex.phploads.
Three Hardening Controls for Legacy and High-Assurance Servers
Whether you're shielding legacy PHP 7.4 / 8.0 / 8.1 workloads during a migration window or enforcing defense-in-depth on patched 8.2 to 8.5 clusters, apply these three controls immediately:
; 1. /etc/php/8.x/fpm/php.ini - Disable upload progress global state & shell execution
session.upload_progress.enabled = 0
expose_php = Off
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec,dl
; 2. /etc/php/8.x/fpm/pool.d/www.conf - Recycle workers frequently to flush heap state
pm.max_requests = 50
Next, strip PHP_SESSION_UPLOAD_PROGRESS requests at your NGINX reverse proxy before they reach the FastCGI socket, and enforce a Linux systemd seccomp syscall filter so the kernel physically blocks php-fpm from executing /bin/sh, bash, curl, or wget:
# 3. Create a systemd kernel sandbox override for php-fpm:
sudo mkdir -p /etc/systemd/system/php8.3-fpm.service.d
cat << 'EOF' | sudo tee /etc/systemd/system/php8.3-fpm.service.d/hardening.conf
[Service]
# Prevent PHP workers from escalating privileges or modifying web root source files
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/www/html/storage /var/lib/php/sessions /var/log
# Block execve/fork of external shells & binaries at the Linux kernel seccomp layer
SystemCallArchitectures=native
SystemCallFilter=~@process @mount @module @raw-io @reboot @swap
EOF
sudo systemctl daemon-reload && sudo systemctl restart php8.3-fpm
Three-Step Post-Exploitation Forensic Audit
- Inspect Live Process Trees for Shell Children Under
php-fpmorhttpd: Runps -ef --forest | grep -E "php-fpm|apache2|httpd" -A 5across every web host. A healthy PHP worker shouldn't ever spawn/bin/sh,bash,python,curl,wget, orncas a child process. - Hunt Error Logs for Intentional Out-of-Memory (
CVE-2026-109618) Probing: Rungrep -i "Allowed memory size of" /var/log/php* /var/log/nginx/error.log /var/log/apache2/error.log. A sudden burst of 20+ OOM errors within seconds from a single client IP is the primary signature of heap-grooming probes targetingPHP2Shell. - Scan Web Directories for Recently Modified Scripts:
Run
find /var/www -type f \( -name "*.php" -o -name "*.phtml" \) -mtime -14 -lsto inspect any executable script created or modified in the last 14 days, and benchmark your perimeter controls using our ISO/IEC 27001 & Zero-Trust Readiness Diagnostic or Enterprise AI & Runtime Security Audit.
AI-assisted vulnerability discovery has permanently compressed the window between source-code auditing and global exploitation. Combining immediate runtime patching with kernel-level syscall containment ensures that memory corruption bugs in web runtimes never become remote shells.
Enjoyed this autopsy? Get next Monday's architecture teardown + SVG blueprints in your inbox.
1 email every Monday morning. Zero marketing fluff — just real VRAM math, MCP security traps, and sovereign lakehouse patterns.
Further Reading
RAG ACL Desynchronization: How Vector Indexes Leak Revoked Docs
Batch-synced vector indexes leak revoked SharePoint and Google Drive files for up to 24 hours. Pre-synthesis SpiceDB Zanzibar checks block stale chunks in 4.8ms.
The Remote MCP Gateway Trap: Why Static Keys Fail OAuth 2.1 Agents
Static bearer tokens and stateless proxies fail to stop cross-modal payload splitting in remote MCP servers. Deploy OAuth 2.1 PKCE token exchange and OPA gates.
What Is an MCP Rug Pull? How Poisoned AI Tools Hijack Agents
An MCP rug pull occurs when a remote AI tool server mutates its schema after approval to steal SSH keys and tokens. Pin SHA-256 tool hashes to block it.
The Invisible Text RAG Trap: Why Zero-Font PDFs Hijack Copilots
Zero-font PDF payloads and white-on-white text bypass standard parsers to poison enterprise RAG indexes. Dual-pass visual OCR attestation strips hidden injections.
Auditing Runtime & Agentic Containment?
If your engineering teams are deploying autonomous tool loops, MCP servers, or evaluation sandboxes, our cyber practice conducts confidential architectural reviews to expose link-local leakage, SSRF vectors, and credential harvesting paths.
Debugging this pattern in your own stack? Ask a Principal Architect directly or request a free 48-hour architecture sanity check: