Someone left the front door of every PHP web server unlocked, and burglars arrived before the locksmith
CVE-2026-109618 and CVE-2026-109619 — collectively dubbed php2shell — are use-after-free vulnerabilities in PHP's core request handling, not in any optional extension or application framework. CVE-2026-109618 triggers when an out-of-memory condition during request parsing leaves internal data partially initialized; cleanup treats invalid data as valid, corrupting the heap. CVE-2026-109619 occurs when a fatal error leaves global state pointing to freed memory; a subsequent request handled by the same worker reuses those stale pointers. Both allow unauthenticated remote code execution via a single crafted multipart/form-data POST request targeting session.upload_progress (enabled by default). The attack lands *before application code executes* on mod_php, meaning application-layer WAFs see nothing. Affected: every PHP version from 5.4 onward. Fixed in 8.2.35, 8.3.36, 8.4.27, and 8.5.12; source backports exist for 7.4/8.0/8.1.
The PHP CNA rates both CVEs at CVSSv4 9.2 (Critical); NVD assigned CVSSv3.1 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H). Red Hat's 8.1 with AC:H is the lone conservative outlier — and it is contradicted by confirmed in-the-wild exploitation observed by Cloudflare, Amazon Threat Intelligence, and watchTowr's honeypot network, plus multiple functional public PoCs. The vendor CRITICAL rating is, for once, entirely justified. If anything, the 9.2 CVSSv4 is modest given the zero-friction attack surface: no auth, default config, every SAPI, every PHP version shipped in the last 14 years. This is the PHP equivalent of Log4Shell — treat it accordingly.
5 steps from start to impact.
Identify target running PHP
X-Powered-By: PHP headers, .php extensions, or PHP-typical response patterns. Shodan shows ~1.9 million internet-facing PHP hosts. Cortex Xpanse telemetry identifies 22.6 million exposed instances. Even without banners, a blind multipart POST costs nothing to attempt.- Target serves HTTP/HTTPS on the internet or accessible network
- None — PHP is the most widely deployed server-side language on the web
http.component:php. Censys: services.software.product: PHP. runZero asset inventory.Craft multipart POST with PHP_SESSION_UPLOAD_PROGRESS
multipart/form-data POST containing a PHP_SESSION_UPLOAD_PROGRESS field. The field value — fully attacker-controlled — is written into PHP's session file at a predictable path (/var/lib/php/sessions/sess_<PHPSESSID>). The upload is designed to stall longer than session.upload_progress.min_freq (default 1s), triggering the vulnerable code path in ext/session/session.c.session.upload_progress.enabled = On(default)- PHP must handle the request (any SAPI)
- session.upload_progress.enabled is ON by default in all PHP distributions
- No application endpoint or route required — the handler fires in the core
PHP_SESSION_UPLOAD_PROGRESS in multipart bodies. Palo Alto Threat ID 543004.Trigger heap use-after-free via OOM or fatal error
longjmp(), leaving partially initialized zval* pointers in the session HashTable. Cleanup writes through stale pointers — a textbook use-after-free. For CVE-2026-109619: a fatal error leaves global state pointing to freed memory; the next request on the same worker reuses it. Both paths corrupt the heap predictably enough for weaponized exploitation, as confirmed by multiple independent researchers and live attacks.- Vulnerable PHP version (5.4 through 8.2.34, 8.3.35, 8.4.26, 8.5.11)
- Red Hat argues AC:H due to precise heap manipulation — but public PoCs and confirmed wild exploitation negate this
Achieve code execution as web worker
www-data, apache, nginx). On mod_php, this occurs *before* any application code runs — no PHP disable_functions, open_basedir, or framework-level security has a chance to intervene. The attacker can read files, establish reverse shells, pivot laterally, or deploy webshells.- Steps 1-3 succeeded
- Worker runs as unprivileged user — but web server access to application data, database credentials, and internal network is typically sufficient for full compromise
Post-exploitation: credential harvesting and lateral movement
wp-config.php, .env, config.php), session tokens from the session store, and API keys from environment variables. CVE-2026-109618 also leaks freed worker memory — similar to Heartbleed — potentially exposing passwords, TLS private keys, and session tokens from *other users' requests*. The attacker pivots to databases, internal services, and adjacent hosts.- Web server has access to databases, internal APIs, or sensitive config files (standard deployment)
- Network segmentation may limit lateral movement, but database credentials in web app configs are near-universal
session.upload_progress.enabled = Off in php.ini and restart PHP-FPM or Apache. This disables the vulnerable code path in ext/session/session.c for CVE-2026-109619 and significantly reduces the attack surface for CVE-2026-109618. Deploy within hours per the noisgate mitigation SLA for CRITICAL with active exploitation — this is a same-day emergency. Most applications do not use upload progress tracking; verify with grep -r 'upload_progress' /var/www/ before deploying.multipart/form-data POST requests containing PHP_SESSION_UPLOAD_PROGRESS in the body. Palo Alto customers should enable Threat ID 543004 (Frontier Virtual Patching, deployed October 20). Deploy within hours. This breaks the known exploit delivery mechanism but may not cover all variants.apt update && apt upgrade php* (Debian/Ubuntu) or dnf update php* (RHEL/Fedora). Verify with php -v or package changelog..php files appearing in web roots. This does not prevent exploitation but ensures rapid detection and containment. Deploy within 24 hours.- Application-layer WAFs and
disable_functions— the vulnerability fires in the PHP core *before* application code or PHP configuration directives likedisable_functionsandopen_basedirtake effect, especially on mod_php. - PHP
memory_limittuning — loweringmemory_limitdoes not prevent the OOM corruption path; it may actually make it *easier* to trigger. - Upgrading only the application framework (WordPress, Laravel, etc.) — the bug is in the PHP interpreter, not in any application. Framework updates are irrelevant.
- Network-level IP blocklisting — exploitation attempts are coming from diverse, rotating IPs. Blocklists cannot keep pace with the volume of scanning.
The supporting signals.
| In-the-Wild Exploitation | Confirmed active. Amazon Threat Intel observed php2shell exploitation attempts on October 17 — four days before disclosure. Cloudflare reported attacks at 07:43 UTC October 21, six hours before the official patch release at 14:00 UTC. watchTowr's Attacker Eye honeypot network confirmed broad exploitation on October 21. |
|---|---|
| Proof-of-Concept Availability | Multiple public PoCs. GitHub repos: eness/php2shell-CVE-2026-109618-CVE-2026-109619-PoC, wakemeiflose/php2shell, ikow/php2shell-poc, syrex1013/php2shell, RemiPelloux/php-session-upload-progress-uaf. Additional PoCs indexed on Sploitus by TheMursalin, aduli198, adityaaravind, alanh0vx, dimasma0305, and BoheSama1999. watchTowr CEO Benjamin Harris confirmed reproduction of CVE-2026-109618 in lab. |
| EPSS Score | Pending verification — both CVEs were disclosed October 21, 2026; EPSS model has not yet ingested sufficient data. Given confirmed wild exploitation, expect >90th percentile once scored. |
| CISA KEV Status | Not listed as of KEV version 2026.10.21 (14:04 UTC). However, CISA has "strongly urged organizations to immediately patch." KEV addition is expected imminently given confirmed exploitation. |
| CVSS Vectors | NVD v3.1: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H → 9.8 Critical. Red Hat v3.1: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H → 8.1 Important (AC:H due to heap manipulation precision). PHP CNA CVSSv4.0: 9.2 Critical. The NVD AC:L assessment is validated by functional PoCs and wild exploitation. |
| Affected Version Ranges | PHP 5.4.0 through 8.2.34, 8.3.0–8.3.35, 8.4.0–8.4.26, 8.5.0–8.5.11. All SAPIs: mod_php, PHP-FPM, php-cgi, FrankenPHP, LiteSpeed. Every PHP release shipped since 2012 is vulnerable. |
| Fixed Versions (incl. distro backports) | Upstream: 8.2.35, 8.3.36, 8.4.27, 8.5.12. Source backports for 7.4/8.0/8.1. Ubuntu 22.04: php8.1 8.1.2-1ubuntu2.28. Ubuntu 24.04: php8.3 8.3.6-0ubuntu0.24.04.13. Debian 12: php8.2 8.2.35-1~deb12u1. Debian 13: php8.4 8.4.27-1~deb13u1. RHEL 9: php:8.2 module updated to 8.2.35. |
| Internet Exposure | Shodan: ~1.9M internet-facing PHP hosts (Henk Poley). Censys: ~1.5M of 2.6M scanned hosts unpatched shortly after disclosure. Cortex Xpanse: 22.6M exposed instances. ~884K hosts running EOL PHP 5/7 branches with no upstream fix. |
| Disclosure Timeline | Aug 1, 2026: Discovery by Ryan Austin (Calif). Aug 11: Coordinated disclosure to PHP. Oct 20, 15:00 UTC: Details leaked publicly. Oct 21, 07:43 UTC: Cloudflare observes first attacks. Oct 21, 14:00 UTC: Official patches released. |
| Reporting Researchers | Ryan Austin (Calif) — primary discoverer. Wiz Research (Sagi Tzadik, Shir Tamari, Nir Ohfeld, Tomer Schwartz) — independent analysis confirming severity. OpenAI researchers — contributed to exploitation chain feasibility using frontier AI models. watchTowr (Benjamin Harris) — lab reproduction and wild exploitation confirmation. |
Sources.
- Tenable: php2shell FAQ (CVE-2026-109618, CVE-2026-109619)
- PHP Foundation: Today's PHP Security Release
- Palo Alto Unit 42: Threat Brief CVE-2026-109618 / CVE-2026-109619
- Sansec: PHP2Shell Research
- Red Hat: CVE-2026-109618
- Rapid7: ETR — Critical Unauthenticated RCE in PHP
- watchTowr: PHP CVE-2026-109618 FAQ
- Help Net Security: PHP2Shell Patched
Why this verdict
- Zero-friction attack surface: Unauthenticated, no special application endpoint, no non-default configuration required.
session.upload_progress.enabledis ON by default. The attacker needs nothing more than HTTP access to any PHP page — or even a non-existent one onmod_php. - Confirmed active exploitation before patch availability: Amazon Threat Intel saw attacks October 17 (4 days pre-disclosure). Cloudflare logged attacks at 07:43 UTC October 21, six hours before the 14:00 UTC patch release. This is not theoretical — it is happening now.
- Weaponized public PoCs eliminate complexity argument: Red Hat's AC:H rating is contradicted by 6+ public GitHub PoC repos, Sploitus listings, and confirmed reproduction by watchTowr and Wiz. The heap manipulation is solved; the exploit is scriptable.
- Role multiplier: PHP is the *canonical* web application runtime — W3Techs estimates ~76% of server-side web uses PHP. High-value roles include: (a) E-commerce platforms (Magento confirmed exploitable — PCI-scoped payment data, fleet of merchant sites), (b) Enterprise CMS (WordPress/Drupal powering corporate and government sites), (c) SaaS API backends (Laravel/Symfony applications holding customer data), (d) Hosting providers (shared hosting = one exploit compromises hundreds of tenants). The bug is in the interpreter core, so *every* PHP deployment is in the blast radius regardless of application. This is fleet-scale by definition. ≥10% of PHP installs occupy high-value roles → verdict floor is CRITICAL.
- Memory disclosure amplifier: CVE-2026-109618 also leaks freed worker memory to the attacker (Heartbleed-style), potentially exposing passwords, session tokens, API keys, and TLS private keys from other users' requests — even without achieving full RCE.
- Massive exposure footprint: 1.9M+ internet-facing hosts on Shodan, 22.6M on Cortex Xpanse. 884K hosts on EOL branches with no upstream fix available. The denominator is enormous.
Why not higher?
A 9.8 is already at the top of the practical scale. The only reason this isn't a theoretical 10.0 is that exploitation on PHP-FPM requires slightly more nuance than mod_php (application-level code paths may be needed), and the web worker process runs as an unprivileged user, adding one hop before root-level system compromise. These are marginal frictions that do not meaningfully reduce urgency.
Why not lower?
Every downgrade argument collapses under scrutiny. Red Hat's AC:H is disproven by confirmed wild exploitation and public PoCs. The 'requires heap manipulation precision' friction is solved — researchers published working exploit chains. No authentication is needed. No non-default configuration is needed. The attack surface is the largest on the internet (PHP powers the majority of web servers). Active exploitation was observed *before* patches were available. There is no credible basis for rating this below CRITICAL.
Crowdsourced verification payload.
Run this script on each target host as any user with read access to the PHP binary (no root required). Invoke with: bash check_php2shell.sh. The script checks all PHP binaries found in PATH and common locations, compares versions against the fixed releases, and also checks the session.upload_progress.enabled setting as a secondary risk indicator.
#!/usr/bin/env bash
# check_php2shell.sh — Detect PHP vulnerable to CVE-2026-109618 / CVE-2026-109619 (php2shell)
# Outputs: VULNERABLE / PATCHED / UNKNOWN per PHP binary found
# Exit codes: 0 = all patched, 1 = at least one vulnerable, 2 = unknown/no PHP found
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'; NC='\033[0m'
FOUND_VULN=0
FOUND_PHP=0
version_ge() {
# Returns 0 if $1 >= $2 using sort -V
printf '%s\n%s' "$2" "$1" | sort -V -C
}
check_php() {
local php_bin="$1"
if [[ ! -x "$php_bin" ]]; then return; fi
FOUND_PHP=1
local full_version
full_version=$( "$php_bin" -r 'echo PHP_VERSION;' 2>/dev/null ) || { echo -e "${YELLOW}[UNKNOWN]${NC} $php_bin — could not determine version"; return; }
local major_minor
major_minor=$( echo "$full_version" | grep -oP '^\d+\.\d+' )
local fixed=""
case "$major_minor" in
8.5) fixed="8.5.12" ;;
8.4) fixed="8.4.27" ;;
8.3) fixed="8.3.36" ;;
8.2) fixed="8.2.35" ;;
8.1|8.0|7.*|5.*) fixed="" ;; # EOL — no upstream fix
*) echo -e "${YELLOW}[UNKNOWN]${NC} $php_bin — unrecognized branch $major_minor ($full_version)"; return ;;
esac
if [[ -z "$fixed" ]]; then
echo -e "${RED}[VULNERABLE]${NC} $php_bin — $full_version (EOL branch, no upstream fix available)"
FOUND_VULN=1
return
fi
if version_ge "$full_version" "$fixed"; then
echo -e "${GREEN}[PATCHED]${NC} $php_bin — $full_version (>= $fixed)"
else
echo -e "${RED}[VULNERABLE]${NC} $php_bin — $full_version (needs >= $fixed)"
FOUND_VULN=1
fi
}
# Check session.upload_progress.enabled
check_sup() {
local php_bin="$1"
local sup
sup=$( "$php_bin" -r 'echo ini_get("session.upload_progress.enabled") ? "On" : "Off";' 2>/dev/null ) || return
if [[ "$sup" == "On" ]]; then
echo -e " ${YELLOW}↳ session.upload_progress.enabled = On (attack surface active)${NC}"
else
echo -e " ${GREEN}↳ session.upload_progress.enabled = Off (mitigated)${NC}"
fi
}
echo "=== php2shell (CVE-2026-109618 / CVE-2026-109619) Check ==="
echo ""
# Collect PHP binaries from PATH and common locations
mapfile -t PHP_BINS < <( { which -a php php8.2 php8.3 php8.4 php8.5 php-fpm php-fpm8.2 php-fpm8.3 php-fpm8.4 php-fpm8.5 2>/dev/null; find /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /opt -maxdepth 3 -name 'php*' -type f -executable 2>/dev/null; } | sort -u )
for bin in "${PHP_BINS[@]}"; do
check_php "$bin"
check_sup "$bin"
done
if [[ $FOUND_PHP -eq 0 ]]; then
echo -e "${YELLOW}[UNKNOWN]${NC} No PHP binaries found on this host."
exit 2
fi
echo ""
if [[ $FOUND_VULN -eq 1 ]]; then
echo -e "${RED}RESULT: VULNERABLE — patch immediately.${NC}"
exit 1
else
echo -e "${GREEN}RESULT: PATCHED — all detected PHP binaries are at fixed versions.${NC}"
exit 0
fi