Most critical vulnerabilities need something to line up before they become dangerous: an exposed admin panel, a plugin nobody patched, a misconfiguration someone made years ago. php2shell needs none of that. It sits in a feature PHP turns on by default, and it needs no login, no session cookie, and no upload form.
On October 20, 2026, details of two critical flaws in the PHP core, CVE-2026-109618 and CVE-2026-109619, leaked publicly before coordinated disclosure. Researchers at watchTowr have since independently reproduced CVE-2026-109618. Defenders are now working against a clock that started before the patches were announced.
Here is what you need to know:
The flaw lives in php_session_rfc1867_callback, PHP’s RFC 1867 upload progress handler in ext/session/session.c. To track upload progress in the session, it caches two raw zval * pointers, post_bytes_processed and current_file_bytes_processed. Both point directly into buckets of a live HashTable. When that HashTable is rebuilt or replaced during a progress update, the cached pointers are left dangling.
The fix replaces the cached zval * pointers with plain zend_long counters. It also introduces php_session_rfc1867_sync_counters(), which re-resolves array entries on every update instead of holding HashTable bucket pointers. A community source patch implementing the same approach is available for organizations that build PHP from source.
Every PHP branch from 5.4 onward is affected. Supported branches have emergency releases; end-of-life branches do not, and each needs its own plan.
| PHP branch | Support status | Affected | Fixed version | What to do |
|---|---|---|---|---|
| PHP 8.5 | Active | < 8.5.12 | 8.5.12 | Upgrade (emergency release) |
| PHP 8.4 | Active | < 8.4.27 | 8.4.27 | Upgrade (emergency release) |
| PHP 8.3 | Active | < 8.3.36 | 8.3.36 | Upgrade (emergency release) |
| PHP 8.2 | Security only | < 8.2.35 | 8.2.35 | Upgrade (emergency release) |
| PHP 8.1 | End of life | All 8.1.x | No official patch | Mitigate, use Extended Lifecycle Support (ELS), or apply a distro backport |
| PHP 8.0 | End of life | All 8.0.x | No official patch | Configuration mitigation is mandatory |
| PHP 7.x | End of life | 7.0 to 7.4 | No official patch | Upgrade or mitigate urgently |
| PHP 5.x | End of life | 5.4.0+ | No official patch | Isolate the host or disable the feature |
For PHP 8.1 and older, your options are vendor or distribution backports, ELS, the community source patch, or the configuration mitigation below. Restrict exposure until you can upgrade, and plan migration of every end-of-life installation to a supported branch.
Upgrade to PHP 8.5.12, 8.4.27, 8.3.36, or 8.2.35, or the equivalent patched package from your OS distribution. Then restart PHP-FPM and the web server so running workers load the fixed binary:
Listing 2: PHP-FPM and Apache Service Restart Commands
# Restart the appropriate service based on your server configuration
sudo systemctl restart php8.3-fpm # Debian/Ubuntu units are versioned; use your branchsudo systemctl restart php-fpm # RHEL-family and most source buildssudo systemctl restart apache2 # or httpd, if PHP runs as an Apache module
Verify the version the web server is actually running on every host. php -v and php -i both report the CLI build, which can differ from what serves traffic. Check the FPM binary directly (php-fpm8.3 -v) or load a phpinfo() page through the web server, then remove it.
Disabling session upload progress neutralizes the attack vector. Normal file uploads keep working; only the session-based upload progress meter is turned off.
In php.ini:
session.upload_progress.enabled = Off
Or per PHP-FPM pool (for example, /etc/php/*/fpm/pool.d/www.conf):
php_admin_flag[session.upload_progress.enabled] = off
FrankenPHP (global Caddyfile block):
{
frankenphp {
php_ini session.upload_progress.enabled 0
}
}
Reload services afterward (systemctl reload php-fpm or apache2). Then confirm through a web-served phpinfo() page that the setting is Off for the web SAPI, not only the CLI.
WAF rules are a layer on top of patching, not a replacement for it. WAFs parse multipart bodies differently from PHP, so evasion is possible. If you’re on Cloudflare, managed rules are already deployed on all plans. The custom rules below use Cloudflare expression syntax; adapt them for ModSecurity or your WAF.
Rule A (surgical; requires body inspection, e.g., Cloudflare Enterprise). Blocks only requests carrying the progress field, so legitimate uploads keep working:
any(http.request.body.form.names[*] == "PHP_SESSION_UPLOAD_PROGRESS")
Rule B (broad; works on all plans). Blocks multipart requests on POST, PUT, and PATCH except on allowlisted upload routes. Adapt the path list to your application:
(any(lower(http.request.headers["content-type"][*]) contains
"multipart/form-data"))
and (http.request.method in {"POST" "PUT" "PATCH"})
and not (http.request.uri.path in {"/upload" "/api/upload" "/wp-admin/async-upload.php"})
Two details matter here:
Do not block on the PHPSESSID cookie or query parameter. It’s PHP’s default session cookie, so blocking it takes the site down for logged-in users. It also isn’t what the exploit uses, and apps that call session_name() bypass it trivially.
With details public since October 20, treat any unpatched, internet-facing PHP host as a hunting priority. Successful exploitation typically shows up as anomalous child processes spawned by PHP worker processes: php-fpm, php-cgi, php-cgi.exe, httpd, or apache2. Alert when one of these parents spawns any of the following:
| Category | Child processes / patterns |
| Interactive shells | /bin/sh, /bin/bash, cmd.exe, powershell.exe |
| Script interpreters | python, perl |
| Network / transfer tools | curl, wget, nc, certutil.exe |
| Reverse-shell patterns | /dev/tcp/, fsockopen, mkfifo |
| Reconnaissance commands | whoami, id, uname -a, ipconfig |
Where to look:
When a flaw lands in a component as foundational as PHP, the patch is the easy part. The hard part is answering three questions quickly and with evidence.
That last question is where most teams lose time. Netenrich Autonomous Security Operations (ASO) correlates WAF, web server, and endpoint signals into a single, actionable incident, so analysts investigate one story instead of triaging thousands of disconnected alerts. It applies the same approach to emerging threats like php2shell: hunting across your environment and confirming whether controls are holding.
Patching closes the door. Proving it stayed closed is what gives security leaders confidence.