October 21, 2026
PHP2Shell: How a Core Runtime Zero-Day Broke the Web (CVE-2026–109618 & CVE-2026–109619)

By Suraj Prajapati
4 min read
Inside the low-level C memory corruption granting unauthenticated RCE across millions of production servers.
A deep dive into how a pair of low-level C runtime bugs created unauthenticated Remote Code Execution across millions of production servers — and how to protect your infrastructure right now.
If your engineering Slack channels or security incident response feeds suddenly blew up over the last 24 hours, you already know why: PHP is facing one of the most severe core runtime vulnerabilities in modern web history.
Tracked as CVE-2026–109618 and CVE-2026–109619 — and rapidly dubbed PHP2Shell by researchers — these paired vulnerabilities allow unauthenticated attackers to achieve complete Remote Code Execution (RCE).
Government portals, major e-commerce platforms, and enterprise infrastructures worldwide are scrambling to deploy emergency patches or take public-facing forms offline.
Here is everything you need to know: why the bug exists, how the exploit chain works under the hood, and how to safeguard your stack.
1. Why the Web Is Panicking
Most PHP vulnerabilities we see in the wild stem from poorly written custom code, insecure framework configurations, or vulnerable third-party plugins (SQL injection, unsafe deserialization, or directory traversal).
PHP2Shell is fundamentally different.
This vulnerability lives directly inside the PHP core C engine. That means:
- Application code doesn't matter: Your code can be 100% bug-free, heavily audited, and free of user-input vulnerabilities.
- Zero authentication required: An attacker does not need an account, an active session, or an administrative dashboard.
- Widespread impact: PHP still powers roughly 70%+ of all internet websites. Whether you are running WordPress, Drupal, Magento, Laravel, Symfony, or a bespoke in-house app, if your web server invokes an unpatched PHP worker process, it is vulnerable.
The flaw affects nearly every standard process execution architecture: PHP-FPM, Apache mod_php, php-cgi, LiteSpeed, and FrankenPHP.
2. Anatomy of the Flaw: What's Actually Happening?
To understand how PHP2Shell works, we have to look past PHP script syntax and look directly at how the Zend Engine manages memory and process lifecycles.
The Trigger: Out-of-Memory (OOM) Panics and longjmp()
When a web server processes an incoming HTTP POST request — specifically complex multipart/form-data payloads with large headers or file boundaries—PHP tracks resource consumption against its configured memory ceilings.
If a payload triggers an unexpected out-of-memory condition or an uncaught fatal parse error, the Zend Engine doesn't gracefully walk back up the standard C call stack. Instead, it relies on low-level non-local jumps via longjmp() to abort the execution thread and reset the request environment.
The Breakdown: The Incomplete Reset
When longjmp() fires during certain phases of multipart parsing and session upload tracking:
- Dangling Pointers: Destruction routines for allocated hashtables, OPcache structures, and upload metadata are bypassed or only partially completed.
- Persistent Worker Pollution: In modern web servers, worker processes (such as PHP-FPM pool workers) do not terminate after a single request. They remain resident in memory to service subsequent incoming web requests.
- Use-After-Free (UAF): The aborted cleanup leaves stale memory references and corrupted heaps in the persistent worker process.
The Shell: From Heap Corruption to Arbitrary Execution
When the subsequent HTTP request reaches that same tainted worker process, the Zend Engine attempts to reference what it expects to be clean global runtime state.
Instead, it reads the remnants of the attacker's previous payload. By carefully manipulating the allocation layout — aligning forged Zend internal structs with executable memory pages — threat actors achieve control of the instruction pointer ($RIP), pivot into shellcode, and spawn an arbitrary reverse shell under the privileges of the web process (www-data, nginx, or apache).
3. Am I Vulnerable?
You are vulnerable if you run any unpatched PHP version on a public or internally accessible server where PHP accepts incoming HTTP traffic.
Officially Patched Upstream Releases
The PHP Group released emergency out-of-cycle security releases across all actively supported branches:
- PHP 8.5.12
- PHP 8.4.27
- PHP 8.3.36
- PHP 8.2.35
The Legacy Problem (PHP 8.1, 8.0, 7.x, 5.x)
Branches like PHP 8.1, 8.0, and earlier are officially End-of-Life (EOL). The PHP Foundation has not released upstream patches for these versions. If your organization is running older distributions, you are entirely reliant on your operating system vendor's extended support backports (e.g., Ubuntu ESM, RHEL Extended Life Cycle Support) or manual enterprise mitigations.
4. How to Remediate Immediately
Step 1: Upgrade PHP (The Real Fix)
Check your current installed version:
Bash
php -vphp -vUpdate your packages via your system package manager and restart your PHP execution pool:
Ubuntu / Debian:
sudo apt update && sudo apt --only-upgrade install php*-fpm php*-cli
sudo systemctl restart php*-fpm
sudo systemctl restart nginx # or apache2sudo apt update && sudo apt --only-upgrade install php*-fpm php*-cli
sudo systemctl restart php*-fpm
sudo systemctl restart nginx # or apache2RHEL / AlmaLinux / Rocky Linux:
sudo dnf upgrade php php-fpm
sudo systemctl restart php-fpmsudo dnf upgrade php php-fpm
sudo systemctl restart php-fpmStep 2: Temporary Emergency Mitigations (If You Cannot Patch Today)
If you manage mission-critical legacy applications, change-frozen production systems, or complex deployment pipelines that cannot be updated in the next hour, apply these stop-gap defenses:
1. Disable Upload Progress in php.ini
Because one of the primary vectors relies on global session tracking during multipart uploads, disable upload progress monitoring across all php.ini configurations:
Ini, TOML
; In /etc/php/*/fpm/php.ini and /etc/php/*/cli/php.ini
session.upload_progress.enabled = 0; In /etc/php/*/fpm/php.ini and /etc/php/*/cli/php.ini
session.upload_progress.enabled = 0Remember to reload/restart your PHP-FPM daemon for this change to take effect.
2. Restrict Multipart Payloads at the Reverse Proxy
If your public endpoints do not require file uploads, reject or heavily constrain multipart/form-data traffic at your Nginx or Cloudflare edge.
Example Nginx snippet:
# Drop or sanitize multipart requests on non-upload routes
if ($http_content_type ~* "multipart/form-data") {
return 403;
}# Drop or sanitize multipart requests on non-upload routes
if ($http_content_type ~* "multipart/form-data") {
return 403;
}3. Adjust Memory Limits on FPM Pools
Because the bug is triggered by forced OOM abort routines during request parsing, temporarily lifting hard-stop memory ceilings or adjusting process recycling intervals (pm.max_requests = 100 or lower) helps force workers to cycle down before exploit chains can stabilize in persistent memory.
Final Thoughts
Vulnerabilities like PHP2Shell serve as a stark reminder of the hidden fragility in the foundational plumbing of the web. When a bug bypasses application logic entirely and targets the underlying process lifecycle, defense-in-depth is your only real safety net:
- Keep your runtimes minimal and jailed (via Docker, systemd sandboxing, or read-only filesystems).
- Run web workers with the lowest possible OS privileges.
- Apply vendor patches immediately when runtime engine zero-days drop.
Don't wait until tomorrow's incident report — check your server fleet, bump your PHP binaries, and restart your workers now.