Someone left the front door's deadbolt wired to unlock whenever a stranger slides a flyer through the mail slot
CVE-2026-109618 is one of a pair of heap use-after-free vulnerabilities (collectively branded *php2shell*) in PHP core's session.upload_progress multipart request handler (php_session_rfc1867_callback in ext/session/session.c). The flaw is triggered by a crafted multipart/form-data POST request that includes a form field named PHP_SESSION_UPLOAD_PROGRESS (the default session.upload_progress.name). Because the upload-progress handler fires during PHP's request-initialization phase — before any application code runs — every PHP endpoint on an affected server is reachable, regardless of the application's own authentication or input validation. The vulnerable code path caches raw pointers into a session HashTable; a timed stall between file-data chunks forces a session re-initialization that frees the HashTable while cached pointers still reference it, yielding a classic use-after-free write primitive. Affected versions span PHP 5.4.0 through 8.5.11. session.upload_progress.enabled is On by default in every standard PHP distribution. Expected fixes: PHP 8.2.35, 8.3.36, 8.4.27, 8.5.12 (released or releasing 2026-10-21 14:00 UTC). PHP 8.1 and all older branches are end-of-life and will receive no official patch.
The vendor's CVSS v4.0 9.2 CRITICAL rating is accurate and, if anything, slightly conservative when mapped to real-world conditions. This is an unauthenticated, network-reachable, zero-click, zero-interaction, default-config RCE affecting the single most deployed server-side runtime on the internet. Cloudflare confirmed early exploitation at 07:43 UTC on October 21 — hours before the patch dropped. Multiple PoC repositories are public (ikow/php2shell-poc, weaponized PHP2Shell tool by Calif.IO). watchTowr independently reproduced the chain. The installed base visible on Shodan is ~2.23 million hosts with PHP banners, of which ~884,000 run end-of-life branches that will never be patched. The vendor severity is justified; our reassessed score is 9.8 reflecting confirmed in-the-wild exploitation and the trivially weaponizable attack surface.
5 steps from start to impact.
Target identification
X-Powered-By: PHP headers or known PHP fingerprints (.php extensions, WordPress/Magento/Drupal signatures). Shodan shows ~2.23 million such hosts. No version check is needed — every version since PHP 5.4.0 with default config is vulnerable. Mass scanning tooling (0xrsydn/php2shell-scan) is already public.- Target runs PHP 5.4.0–8.5.11 with
session.upload_progress.enabled=On(default) - Target accepts HTTP POST requests on any PHP-handled URI
- None — default PHP installations are vulnerable
- WAF or reverse proxy might filter multipart traffic, but most do not inspect
session.upload_progress.namefield content
http.component:PHP or Censys query (see silascutler/php2shell-censys gist). Nessus/Qualys plugin availability pending; version-based detection is straightforward.Craft malicious multipart POST
multipart/form-data POST body containing a form field whose name matches session.upload_progress.name (default: PHP_SESSION_UPLOAD_PROGRESS). The request also includes a file upload part with a specially crafted boundary and timing stall. The PHP2Shell weaponized tool (Calif.IO) and ikow/php2shell-poc both automate this step. No valid session cookie or authentication token is required.- Knowledge of the default
session.upload_progress.namevalue (public, documented in php.ini) - Ability to send a multipart POST to any PHP URI
- A WAF that strips or blocks the
PHP_SESSION_UPLOAD_PROGRESSfield name in multipart bodies would break this step - Cloudflare deployed emergency managed rules (Generic Rules - Anomalous Execution) on 2026-10-21, but these provide partial coverage only
PHP_SESSION_UPLOAD_PROGRESS field to unusual endpoints. Cloudflare managed ruleset logs.Trigger session handler UAF
php_session_rfc1867_callback() to process the upload-progress field. The attacker's timed stall between file data chunks causes PHP to re-initialize the session, freeing the internal HashTable. Cached pointers from the earlier callback invocation now reference freed memory. This is the use-after-free (CWE-416) primitive. The entire sequence occurs in PHP core before any application index.php or framework router executes.- PHP's multipart parser processes the request (guaranteed for any
Content-Type: multipart/form-dataPOST) session.upload_progress.enabledis On (default)
- Disabling
session.upload_progress.enabledblocks this specific CVE's path (but research indicates the sibling CVE-2026-109619 targets the multipart parser itself and is NOT blocked by this setting) - ASLR and PIE on modern Linux make heap layout less predictable, but the PoC reportedly handles this with heap spray
process-watcher) can detect the post-exploitation phase.Achieve code execution as PHP worker
__destruct handler. PHP executes attacker-supplied code in the context of the PHP-FPM worker, php-cgi, or Apache mod_php process — typically running as www-data, apache, or nginx. The attacker now has arbitrary command execution on the server at the privilege level of the web server user.- Successful heap manipulation from step 3
- PHP's
disable_functionsandopen_basedirare bypassed because the exploitation occurs in C-level internals, not in PHP userland - SELinux/AppArmor confined profiles for php-fpm *may* limit post-exploitation capabilities but do not prevent the initial code execution
sh, bash, cmd.exe), download utilities (curl, wget, certutil.exe), or reconnaissance commands (whoami, id, ipconfig). Sigma rules for web shell process trees.Post-exploitation and persistence
wp-config.php yields DB credentials and auth salts. Attackers may inject payment skimmers (Sansec's primary concern), steal customer PII, or pivot to internal networks if the web server has LAN connectivity.- Code execution achieved in step 4
- Network segmentation between DMZ and internal network limits lateral movement
- Database credential rotation and least-privilege DB accounts limit data exfiltration scope
session.upload_progress.enabled=0 to php.ini or per-pool via php_admin_flag[session.upload_progress.enabled] = off in FPM pool configs. Restart PHP-FPM/Apache. This disables the vulnerable php_session_rfc1867_callback code path for CVE-2026-109618 specifically. Warning: research (J4ck3LSyN/lilting.ch) indicates this setting blocks only ONE of the two php2shell CVEs; the sibling CVE-2026-109619 may still be reachable via the multipart parser itself. Apply within hours — this is an active-exploitation scenario overriding the standard noisgate mitigation SLA.PHP_SESSION_UPLOAD_PROGRESS. Syhunt published Apache mitigation rules. Note: WAF bypass is possible if the attacker can reach the origin directly, and Cloudflare's own advisory notes these rules provide partial coverage. Apply within hours.apt update && apt upgrade php8.x on Debian/Ubuntu; dnf update php on RHEL/Fedora). Given confirmed active exploitation, the noisgate mitigation SLA (patch/mitigate immediately, within hours) applies — do not wait for the standard 3-day CRITICAL window.session.upload_progress.enabled=0 as a partial mitigation. Plan the upgrade within the noisgate remediation SLA of 90 days for CRITICAL.process-watcher to alert on PHP worker processes (php-fpm, php-cgi, apache2, httpd) spawning child processes (sh, bash, cmd.exe, curl, wget, python, perl, nc). This does not prevent exploitation but detects successful post-exploitation within seconds, enabling rapid containment. Deploy immediately.disable_functionsin php.ini — does not help because the exploitation occurs in PHP's C-level internals (heap UAF), not via PHP userland function calls. The attacker's code runs beforedisable_functionsrestrictions are applied.open_basedirrestrictions — same rationale; the memory corruption and code execution happen below the PHP engine's permission model.memory_limit = -1or similar memory tuning — explicitly confirmed by multiple sources as ineffective against these vulnerabilities.- IP-based allowlisting alone — the attack can originate from any IP, and botnet-sourced exploitation makes IP blocking a game of whack-a-mole. Use in combination with field-level WAF rules, not as a standalone control.
- PHP
session.use_strict_mode=1alone — while recommended as defense-in-depth, this does not prevent the UAF trigger in the upload-progress handler; the vulnerability fires before session validation completes.
The supporting signals.
| In-the-Wild Exploitation | Confirmed. Cloudflare reported early attacks against PHP sites at 07:43 UTC on 2026-10-21, hours before the patch release. Cloudflare deployed emergency WAF rules (Generic Rules - Anomalous Execution). Sansec warns of imminent mass exploitation targeting e-commerce platforms. Not yet in CISA KEV as of 2026-10-21. |
|---|---|
| Proof-of-Concept Availability | Public and weaponized. ikow/php2shell-poc (researcher Qi Deng, published 2026-10-21) contains verification PoCs and lab setups. A weaponized exploit tool named *PHP2Shell* credited to Calif.IO is circulating. Additional repos on Sploitus: Blue-B/php2shell-guard, 0xrsydn/php2shell-scan, cereZ23/php-up-2026, darkfibr/php2shell-detect. watchTowr independently confirmed reproduction. Syhunt published Apache mitigation rules. |
| EPSS Score | Not yet scored — NVD record is still RESERVED. Given the pre-auth RCE nature, default-on configuration, and confirmed exploitation, expect EPSS to land in the 99th+ percentile once scored. |
| CISA KEV Status | Not listed as of 2026-10-21 10:00 UTC. Given confirmed exploitation by Cloudflare, KEV listing is likely imminent. |
| CVSS Vector (estimated v3.1) | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8. Vendor rated CVSS v4.0 9.2. Network-reachable, low complexity (weaponized PoC exists), no privileges, no interaction, full CIA impact. The v3.1 mapping yields 9.8 because v4.0's supplemental metrics likely account for attack requirements that compress the score. |
| Affected Versions | PHP 5.4.0 through 8.5.11 — every version that ships session.upload_progress (enabled by default since PHP 5.4). PHP 8.1 and all older branches are EOL and will receive no official fix. |
| Fixed Versions | PHP 8.2.35, 8.3.36, 8.4.27, 8.5.12 — scheduled for release 2026-10-21 14:00 UTC. Distro backports (Debian, Ubuntu, RHEL/CentOS, SUSE, Alpine) are pending; track php8.x packages in your distro's security tracker. |
| Internet Exposure (Shodan) | ~2.23 million hosts advertising X-Powered-By: PHP headers. ~1.5M disclose a version. ~884,000 hosts (~59% of versioned) run unsupported branches (PHP 5.x, 7.x, 8.0, 8.1) that will never receive a patch. Top countries: US (228,770), China, Japan (55,460), Germany (45,835). Censys query published by silascutler. |
| Disclosure Timeline | 2026-10-20: php.net pre-announcement of emergency release. Same day: details leaked, PHP2Shell tool surfaces, CERT-EU assigns CVE IDs. 2026-10-21 07:43 UTC: Cloudflare confirms early exploitation. 2026-10-21 14:00 UTC: scheduled patch release. |
| Reporting Researchers | Original discovery not publicly credited. Exploit tool *PHP2Shell* attributed to Calif.IO. PoC research by Qi Deng (ikow). Independent reproduction by watchTowr. Analysis by Sansec, Huntress, J4ck3LSyN (lilting.ch). Mitigation tooling by Syhunt. |
Sources.
- Sansec — Critical PHP RCE: CVE-2026-109618 and CVE-2026-109619
- Huntress — Rapid Response: php2shell Leaked Critical PHP Vulnerabilities
- php.net — Upcoming Critical PHP Security Release announcement
- Phoenix Security — PHP Core RCE CVE-2026-109618 & 109619 exposure analysis
- ikow/php2shell-poc — PoC and analysis (GitHub)
- Brandefense — PHP2Shell advisory and timeline
- lilting.ch — Disabling session.upload_progress before the php2shell patch
- Syhunt — Emergency Apache mitigation toolkit for php2shell
Why this verdict
- Pre-auth, zero-click, network-reachable RCE on default config. The attack requires only the ability to send an HTTP POST to any PHP endpoint. No authentication, no user interaction, no non-default settings. CVSS:3.1 base is 9.8. The vendor's v4.0 9.2 is fair within that framework; under v3.1 this maps higher.
- Confirmed in-the-wild exploitation. Cloudflare detected attacks at 07:43 UTC on 2026-10-21, before the patch was available. This is not theoretical — exploitation is happening now. Multiple weaponized PoCs are public.
- Installed base is the largest server-side runtime on the internet. Shodan shows 2.23M PHP hosts with public banners. The true number is far higher (many suppress headers). WordPress alone powers ~40% of websites. The blast radius of a PHP core RCE is effectively internet-scale.
- Role multiplier: web application tier. PHP is canonically an internet-facing application runtime. ~100% of PHP installations exist to serve HTTP traffic. The affected component (
session.upload_progress) is enabled by default and fires for *any* multipart POST — the application doesn't need to use upload progress. On e-commerce platforms (Magento, WooCommerce, Shopware), the blast radius is payment-card theft and mass PII exfiltration (supply-chain-scale for Sansec's threat model). On WordPress sites with DB credentials inwp-config.php, the blast radius extends to full database compromise. On hosting providers, a single shared-hosting server may run hundreds of PHP sites. The high-value-role floor is CRITICAL because PHP *is* the network-edge application layer for the majority of the web. - Role multiplier: hosting infrastructure. Shared hosting providers (GoDaddy, Hostinger, etc.) run thousands of customer sites per PHP-FPM pool. A single exploitation yields access to all co-tenanted sites on that instance — fleet-scale blast radius.
- 884,000 internet-facing hosts on EOL PHP branches will never receive an official patch. These hosts remain permanently exploitable unless they upgrade major PHP versions or apply the ini mitigation. This is a long-tail catastrophe for the internet.
- Zero friction in the attack chain. No step requires elevated access, non-default config, or chaining with another vulnerability. The attack is a single HTTP request to any PHP endpoint.
disable_functionsandopen_basedirdo not help because exploitation occurs in PHP's C internals, not userland.
Why not higher?
A 9.8 is effectively the ceiling for a use-after-free RCE. A 10.0 would require scope change (e.g., hypervisor escape) or a wormable propagation mechanism. While the blast radius is enormous, each exploitation instance is a single-host compromise at the web-server privilege level, not an automatic lateral-movement chain. Post-exploitation still requires attacker effort to pivot.
Why not lower?
Every downward-pressure factor is absent. The attack is pre-auth (no privilege requirement), default-config (no non-standard setup needed), zero-click (no user interaction), network-reachable (internet-facing by definition), and actively exploited in the wild with public weaponized tooling. The affected software is the most widely deployed server-side runtime on earth. There is no realistic friction to cite. Dropping below CRITICAL would be negligent given confirmed exploitation and the scope of the installed base.
Crowdsourced verification payload.
Run this script on each target PHP host as any user who can execute php -v and read php.ini. Example: chmod +x php2shell-check.sh && ./php2shell-check.sh. No root required unless PHP is installed in a restricted path. The script checks the installed PHP version against known-fixed releases and inspects the session.upload_progress.enabled runtime setting.
#!/usr/bin/env bash\n# php2shell-check.sh — CVE-2026-109618 exposure checker\n# Usage: ./php2shell-check.sh\n# Exit codes: 1=VULNERABLE, 0=PATCHED, 2=UNKNOWN\n\nset -euo pipefail\n\nPHP_BIN=\"${PHP_BIN:-php}\"\n\nif ! command -v \"$PHP_BIN\" &>/dev/null; then\n echo \"UNKNOWN — php binary not found in PATH. Set PHP_BIN=/path/to/php and retry.\"\n exit 2\nfi\n\nVERSION_RAW=$($PHP_BIN -r 'echo PHP_VERSION;' 2>/dev/null)\nif [[ -z \"$VERSION_RAW\" ]]; then\n echo \"UNKNOWN — could not determine PHP version.\"\n exit 2\nfi\n\nMAJOR=$(echo \"$VERSION_RAW\" | cut -d. -f1)\nMINOR=$(echo \"$VERSION_RAW\" | cut -d. -f2)\nPATCH=$(echo \"$VERSION_RAW\" | cut -d. -f3 | sed 's/[^0-9].*//')\n\n# Check session.upload_progress.enabled\nSUP=$($PHP_BIN -r 'echo ini_get(\"session.upload_progress.enabled\");' 2>/dev/null || echo \"unknown\")\n\necho \"PHP Version: $VERSION_RAW\"\necho \"session.upload_progress.enabled: $SUP\"\necho \"---\"\n\n# Fixed versions: 8.2.35, 8.3.36, 8.4.27, 8.5.12\nVULNERABLE=0\n\nif (( MAJOR < 8 )); then\n echo \"VULNERABLE — PHP $VERSION_RAW is end-of-life and will not receive a patch for CVE-2026-109618.\"\n echo \"Action: Upgrade to PHP 8.2.35+, 8.3.36+, 8.4.27+, or 8.5.12+.\"\n VULNERABLE=1\nelif (( MAJOR == 8 )); then\n case \"$MINOR\" in\n 0|1)\n echo \"VULNERABLE — PHP $VERSION_RAW is end-of-life (8.0/8.1). No official patch available.\"\n echo \"Action: Upgrade to a supported PHP branch and apply the fix.\"\n VULNERABLE=1\n ;;\n 2)\n if (( PATCH < 35 )); then\n echo \"VULNERABLE — PHP $VERSION_RAW is below the fixed version 8.2.35.\"\n VULNERABLE=1\n else\n echo \"PATCHED — PHP $VERSION_RAW includes the fix (>= 8.2.35).\"\n fi\n ;;\n 3)\n if (( PATCH < 36 )); then\n echo \"VULNERABLE — PHP $VERSION_RAW is below the fixed version 8.3.36.\"\n VULNERABLE=1\n else\n echo \"PATCHED — PHP $VERSION_RAW includes the fix (>= 8.3.36).\"\n fi\n ;;\n 4)\n if (( PATCH < 27 )); then\n echo \"VULNERABLE — PHP $VERSION_RAW is below the fixed version 8.4.27.\"\n VULNERABLE=1\n else\n echo \"PATCHED — PHP $VERSION_RAW includes the fix (>= 8.4.27).\"\n fi\n ;;\n 5)\n if (( PATCH < 12 )); then\n echo \"VULNERABLE — PHP $VERSION_RAW is below the fixed version 8.5.12.\"\n VULNERABLE=1\n else\n echo \"PATCHED — PHP $VERSION_RAW includes the fix (>= 8.5.12).\"\n fi\n ;;\n *)\n echo \"UNKNOWN — PHP 8.$MINOR is not a recognized branch. Verify manually.\"\n exit 2\n ;;\n esac\nelse\n echo \"UNKNOWN — PHP $MAJOR.x is newer than expected. Verify manually.\"\n exit 2\nfi\n\nif (( VULNERABLE == 1 )); then\n if [[ \"$SUP\" == \"\" || \"$SUP\" == \"0\" ]]; then\n echo \"NOTE: session.upload_progress.enabled is OFF — partial mitigation for CVE-2026-109618 is in place.\"\n echo \"WARNING: This does NOT mitigate the sibling CVE-2026-109619. Patching is still required.\"\n else\n echo \"WARNING: session.upload_progress.enabled is ON (default). System is fully exposed.\"\n echo \"Immediate action: Set session.upload_progress.enabled=0 in php.ini and restart PHP.\"\n fi\n exit 1\nelse\n exit 0\nfi