← Back to Feed CACHED · 2026-10-21 10:23:43 · CACHE_KEY CVE-2026-109619
CVE-2026-109619 · CWE-416 · Disclosed 2026-10-20

PHP core session.upload_progress pre-auth heap use-after-free via __destruct reentry in php_session_rfc1867_callback

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

The front door to every PHP site just got a skeleton key, and the locksmith shows up at 2 PM

CVE-2026-109619 is a heap use-after-free (CWE-416) in PHP's php_session_rfc1867_callback() function within ext/session/session.c. When session.upload_progress.enabled is On — the default since PHP 5.4.0 — a crafted multipart/form-data POST containing a field matching session.upload_progress.name (default: PHP_SESSION_UPLOAD_PROGRESS) triggers the callback *before any application code runs*. The callback caches raw zval * pointers into HashTables that are freed during session re-decode; a subsequent upload-progress update dereferences the stale pointer via zval_ptr_dtor, and an attacker who wins the heap race can redirect execution through zend_call_function(). Third-party research (ikow/php2shell-poc, confirmed by ASAN) attributes this specifically to var_destroy __destruct reentry. All PHP versions from 5.4.0 through 8.5.11 are affected. Patches are expected as PHP 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12, releasing today (2026-10-21 14:00 UTC). PHP 8.1 and earlier are EOL with no official fix. Note: The two php2shell CVEs (CVE-2026-109618 and CVE-2026-109619) are not yet publicly distinguished by PHP; third-party analysis attributes this CVE to the upload-progress handler UAF and CVE-2026-109618 to a separate multipart-parser flaw.

The vendor's CVSS v4.0 9.2 CRITICAL rating is warranted and possibly conservative. This is a pre-authentication, zero-click, network-reachable RCE in the runtime itself — not in a CMS plugin or optional extension, but in the PHP interpreter's default request-parsing path. The attack surface is every PHP process that accepts HTTP POST requests, which encompasses roughly 2.2 million internet-facing hosts by Censys count and untold millions behind reverse proxies. The 9.2 (rather than 10.0) reflects that heap-corruption exploitation is not deterministic across all builds and architectures, which is fair. But with a weaponized tool (PHP2Shell / Calif.IO) already circulating and Cloudflare confirming early attacks as of 07:43 UTC today, the practical exploitability gap is closing rapidly. noisgate holds this at CRITICAL 9.2 — unchanged from vendor.

"Pre-auth RCE in PHP core via default session config — active exploitation confirmed, patch today"
02 · The Attack Path

5 steps from start to impact.

STEP 01

Attacker identifies PHP target

The attacker locates any internet-facing endpoint running PHP. HTTP response headers (X-Powered-By: PHP/x.x.x), default error pages, and known PHP applications (WordPress, Laravel, Drupal, Magento) are trivial to fingerprint. Censys reports ~2.23 million hosts advertising PHP. Tools like httpx or Shodan dorks (http.component:php) enumerate targets at scale.
Conditions required:
  • Network access to a host running PHP (HTTP/HTTPS)
Where this breaks in practice:
  • None — PHP is the most deployed server-side runtime on the internet
Detection/coverage: Shodan/Censys queries: http.component:php; runZero asset inventory for PHP version fingerprinting
STEP 02

Craft malicious multipart POST

The attacker constructs a multipart/form-data POST request containing a field named PHP_SESSION_UPLOAD_PROGRESS (or whatever session.upload_progress.name is set to — the default suffices). This triggers php_session_rfc1867_callback() during SAPI request initialization, *before the application's index.php is even entered*. The PHP2Shell tool (credited to Calif.IO) automates this payload generation. The ikow/php2shell-poc repository on GitHub provides a research PoC with lab setup.
Conditions required:
  • Target PHP has session.upload_progress.enabled = On (the default)
  • Target accepts multipart/form-data POST on any endpoint
Where this breaks in practice:
  • WAF rules blocking multipart POSTs with PHP_SESSION_UPLOAD_PROGRESS field — Cloudflare deployed rules 07:43 UTC today
  • Servers behind strict reverse proxies that strip or reject unexpected Content-Type headers
Detection/coverage: WAF logs for multipart POSTs with PHP_SESSION_UPLOAD_PROGRESS field; Cloudflare Managed Ruleset 'Generic Rules - Anomalous Execution'
STEP 03

Trigger session re-decode and UAF

The crafted payload forces PHP's session handler to decode, then re-decode session data during the same request lifecycle. The first decode populates zval * pointers in the upload-progress bookkeeping HashTable. The re-decode calls zval_ptr_dtor on the prior tree, freeing those entries — but the callback still holds stale references. This is the use-after-free primitive. ASAN-confirmed by researcher Qi Deng (@ikow) in the php2shell-poc writeup. The var_destroy path allows __destruct reentry, giving the attacker a controlled function-call primitive.
Conditions required:
  • Step 2 payload reaches the PHP SAPI parser
  • Default session serializer configuration
Where this breaks in practice:
  • Heap layout is architecture-dependent; exploitation reliability varies across PHP builds, operating systems, and memory allocators (glibc vs jemalloc vs musl)
  • ASLR and NX on modern Linux reduce one-shot reliability
Detection/coverage: PHP-FPM segfaults correlated with multipart POST requests; ASAN/Valgrind in staging environments
STEP 04

Achieve code execution as PHP process user

By reclaiming the freed heap allocation with attacker-controlled data, the exploit redirects execution through zend_call_function(). The attacker now executes arbitrary code as the PHP-FPM worker user (typically www-data, apache, or nginx). From here, standard post-exploitation applies: reading wp-config.php / .env for database credentials, dropping webshells, establishing reverse shells, or pivoting to internal services. The PHP2Shell tool reportedly automates this end-to-end.
Conditions required:
  • Successful heap reclamation (previous step)
Where this breaks in practice:
  • Not all PHP builds are equally exploitable — statically linked, hardened, or Alpine/musl builds may resist the current public exploit
  • SELinux/AppArmor in enforcing mode can constrain post-exploitation
Detection/coverage: Unexpected child processes spawned by PHP workers: sh, bash, curl, wget, nc, python, perl, certutil.exe; Sysdig Falco / Aqua / EDR rules for container escape patterns
STEP 05

Lateral movement and data exfiltration

With code execution on the web server, the attacker reads database connection strings, API keys, session tokens, and TLS private keys stored on disk. In shared hosting environments, this can compromise every tenant on the host. In cloud-native deployments, the PHP process often has access to instance metadata endpoints (169.254.169.254), enabling cloud credential theft and further pivot. WordPress installations alone represent ~40% of all websites.
Conditions required:
  • Code execution achieved (Step 4)
  • Database credentials or cloud metadata accessible from PHP process context
Where this breaks in practice:
  • Network segmentation between web tier and database tier
  • Instance metadata service v2 (IMDSv2) requiring PUT token
  • Secrets management (Vault, AWS Secrets Manager) with short-lived tokens rather than disk-stored credentials
Detection/coverage: Outbound connections from PHP worker processes to unusual destinations; database query anomalies; cloud audit logs for metadata access from web-tier instances
03 · Compensating Control

1
CRITICAL 9.2→HIGH 7.5
SEVERITY REDUCED
Set session.upload_progress.enabled=0 in php.ini immediately — This disables the specific callback (php_session_rfc1867_callback) that contains the UAF for CVE-2026-109619, eliminating the trigger path. Apply via php_admin_flag[session.upload_progress.enabled] = off in each FPM pool config, or session.upload_progress.enabled = 0 in php.ini. Note: This mitigates CVE-2026-109619 specifically but third-party analysis suggests CVE-2026-109618 (the sibling multipart parser flaw) remains unaffected by this setting. Given active exploitation, deploy within hours — not the standard noisgate CRITICAL mitigation SLA of 3 days. Restart PHP-FPM/Apache after changing.
2
CRITICAL 9.2→HIGH 7.0
SEVERITY REDUCED
Deploy WAF rules blocking multipart POSTs with PHP_SESSION_UPLOAD_PROGRESS field — Configure your WAF (Cloudflare, AWS WAF, ModSecurity, F5 ASM) to inspect multipart/form-data POST bodies and block requests containing the PHP_SESSION_UPLOAD_PROGRESS field name. Cloudflare has already deployed managed rules ('Generic Rules - Anomalous Execution'). For ModSecurity: SecRule REQUEST_BODY "PHP_SESSION_UPLOAD_PROGRESS" "id:1,phase:2,deny,status:403". This is a defense-in-depth layer — sophisticated attackers may find bypasses or use non-default field names if session.upload_progress.name was customized. Deploy within hours alongside the php.ini change.
3
CRITICAL 9.2→CRITICAL 8.5
Block direct access to PHP origin servers; enforce CDN/proxy-only ingress — Ensure your PHP servers are not directly reachable from the internet — all traffic should flow through your CDN or reverse proxy. If attackers can bypass Cloudflare/CDN by hitting the origin IP directly, WAF rules are useless. Validate that origin server firewalls only allow inbound HTTP from your CDN's IP ranges. This is a standard hardening measure but becomes critical when WAF is your primary mitigation.
4
CRITICAL 9.2→IGNORE 0.0
SEVERITY REDUCED
Apply vendor patch immediately upon release (14:00 UTC today) — The PHP security releases (8.2.35 / 8.3.36 / 8.4.27 / 8.5.12) are the only complete remediation. Given active exploitation, this overrides the standard noisgate CRITICAL remediation SLA of 90 days — treat this as an emergency patch with a 24-48 hour deployment window. For EOL branches (PHP 8.1 and earlier), no official patch will be issued; migration to a supported branch is mandatory. Coordinate with hosting providers and distro maintainers for backport timelines.
5
CRITICAL 9.2→CRITICAL 9.2
Monitor PHP worker processes for anomalous child process spawning — Deploy detection rules for PHP-FPM/Apache workers spawning shell processes (bash, sh, cmd.exe), download tools (curl, wget, certutil), or scripting interpreters (python, perl). Sysdig Falco, CrowdStrike Falcon, and similar EDR/runtime security tools can alert on this. This does not prevent exploitation but ensures you detect successful compromise within minutes rather than days. Deploy within hours.
What doesn't work
  • Application-level input validation — the vulnerability fires during PHP's SAPI request initialization, *before* any application code (index.php, .htaccess handlers, framework middleware) executes. No amount of Laravel validation or WordPress nonce checking helps.
  • session.use_strict_mode=1 alone — strict mode prevents session fixation by rejecting uninitialized session IDs, but the UAF occurs in the upload-progress callback regardless of session mode. It is a useful hardening measure but does not address the memory-safety flaw.
  • Disabling file_uploads in php.ini — the multipart parser runs for any multipart/form-data POST regardless of whether file uploads are enabled. The session.upload_progress callback triggers on the field name, not on an actual file upload. Setting file_uploads=Off does not prevent the vulnerable code path from executing.
  • PHP open_basedir / disable_functions — these restrict what PHP *scripts* can do, but the vulnerability achieves code execution inside the PHP interpreter itself, at the C level, bypassing all userland restrictions.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationActive. Cloudflare CTO Dane Knecht confirmed early attacks at 07:43 UTC on 2026-10-21. Patchstack reports exploitation from multiple hosting partners starting ~07:34 UTC. Several Japanese platforms and The SEO Framework went offline preemptively. Huntress had *not* observed exploitation in their partner environments as of Oct 20 evening, but the window has since closed.
Proof of ConceptPublic. ikow/php2shell-poc by Qi Deng (@ikow) — ASAN-confirmed UAF PoC with lab setup and writeup at qideng.substack.com. Weaponized tool PHP2Shell credited to Calif.IO circulating privately (sploitus.com indexes it). syhunt/php2shell-protection provides Apache mitigation and testing toolkit. watchTowr CEO Benjamin Harris confirmed reproduction of CVE-2026-109618; CVE-2026-109619 reproduction implied but not separately confirmed.
EPSS ScoreNot yet available. NVD record is RESERVED; EPSS has not scored this CVE. Given active exploitation and public PoC, expect EPSS >0.9 (top 1%) once scored.
CISA KEV StatusNot listed as of 2026-10-21 10:00 UTC. Given confirmed exploitation, KEV addition is likely imminent.
CVSS VectorVendor published CVSS v4.0 9.2 only. No v3.1 vector published. Estimated v3.1 equivalent: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H (8.1) — AC:H reflects heap manipulation requirement. Some researchers (watchTowr, Florian Roth) claim effective 10.0 given default-config reachability.
Affected VersionsPHP 5.4.0 through 8.5.11 — the session.upload_progress feature was introduced in 5.4.0. PHP 8.1 and earlier are EOL with no official fix. Supported branches: 8.2, 8.3, 8.4, 8.5.
Fixed VersionsExpected: PHP 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 — release scheduled 2026-10-21 14:00 UTC. Unconfirmed until published. Distro backports (Debian, Ubuntu, RHEL, SUSE) will follow on their own timelines. ~884,000 hosts run unsupported PHP branches that will never receive an official fix.
Internet ExposureCensys/Phoenix Security: ~2.23 million hosts advertising PHP headers. ~884,000 on unsupported branches (5.x/7.x/8.0/8.1). Top regions: US (228K), Japan (55K), Germany (46K). Major hosting: Amazon, Hostinger, Alibaba Cloud. True exposure is higher — many PHP sites behind proxies don't expose version headers.
Disclosure Timeline2026-10-20: php.net announces upcoming critical release. Same day: details leak via Hacker News (user 'petecooper', post deleted). CERT-EU assigns CVE-2026-109618 and CVE-2026-109619. 2026-10-21 07:34 UTC: Patchstack receives exploitation reports. 07:43 UTC: Cloudflare confirms attacks and deploys WAF rules. 14:00 UTC: Patch release scheduled.
Credited ResearchersQi Deng (@ikow) — php2shell-poc research PoC. Calif.IO — PHP2Shell weaponized exploit tool. watchTowr (CEO Benjamin Harris) — independent reproduction. Sansec — early public analysis. CERT-EU — CVE assignment.

Sources.

  1. Sansec — Critical PHP RCE: CVE-2026-109618 and CVE-2026-109619
  2. Huntress — Rapid Response: php2shell Leaked Critical PHP Vulnerabilities
  3. ikow/php2shell-poc — CVE-2026-109618/109619 Exploit Research PoC
  4. Patchstack — Major Security Alert: PHP Vulnerable to Critical RCE, Attacks Ongoing
  5. BleepingComputer — PHP Warns Admins of Two Critical Flaws, Emergency Patches Coming
  6. Phoenix Security — PHP Core RCE: Two Critical Remote Code Execution Flaws
  7. Brandefense — PHP2Shell: PHP RCE CVE-2026-109618, 109619
  8. syhunt/php2shell-protection — Emergency Apache Mitigation Toolkit
05 · The Call

Final Verdict
= UNCHANGED to CRITICAL (9.2/10)

Why this verdict

  • Zero friction on initial access: This is a pre-auth, zero-click, network-reachable RCE triggered by a single HTTP POST. No credentials, no user interaction, no application-specific conditions. The only prerequisite — session.upload_progress.enabled=On — is the factory default on every PHP installation since 5.4.0 (2012). This means virtually 100% of unpatched PHP installations are vulnerable out of the box.
  • Attack surface is historically vast: PHP powers the backend of an estimated 75%+ of websites with known server-side technology. Censys identifies 2.23 million directly fingerprinted hosts; the true number behind CDNs and proxies is far larger. The vulnerability is in the *interpreter itself*, not in any specific application — WordPress, Laravel, Drupal, Magento, custom apps, and API backends are all equally exposed if they run PHP.
  • Weaponized exploit in circulation with confirmed exploitation: The PHP2Shell tool is already weaponized and circulating. Cloudflare and Patchstack hosting partners confirmed active attacks starting ~07:30 UTC today, hours before the patch release. This is no longer a theoretical risk — it is an active incident.
  • Heap UAF exploitation uncertainty provides the only downward pressure: Memory corruption exploits are not deterministic across all builds, architectures, and memory allocators. ASLR and NX reduce one-shot reliability. This is why the vendor rated 9.2 rather than 10.0, and why we do not upgrade above 9.2. However, the public PoC and weaponized tool demonstrate that this gap is surmountable for a motivated attacker across common Linux/x86_64 PHP-FPM deployments.
  • Role multiplier: PHP is not a single-role component — it IS the web application tier for the majority of the internet. (a) *Low-value role:* dev sandboxes, local MAMP/WAMP — exploitation yields local code exec, limited blast radius. (b) *Typical role:* production web servers behind load balancers — exploitation yields access to database credentials, session stores, API keys, user data; blast radius is host-to-tenant-scale. (c) *High-value role:* shared hosting platforms (one PHP process serves hundreds of tenants — compromise is fleet-scale), WordPress multisite (domain-scale), e-commerce backends processing payment data (PCI-scope breach), internal admin panels (identity/privilege escalation), and CI/CD systems with PHP tooling. Since PHP's canonical deployment IS the web-facing application tier serving production traffic — easily >80% of installations — the high-value role floor is CRITICAL. The affected component is definitionally an internet-edge runtime processing untrusted input.

Why not higher?

The vendor's 9.2 accurately reflects that heap use-after-free exploitation is architecture-dependent and not 100% reliable on all builds. Musl-based Alpine containers, statically linked PHP, and non-standard memory allocators can break the current public exploit chain. A 10.0 would imply trivial, deterministic exploitation on every target, which overstates the current state — though reliability is improving as researchers refine heap-grooming techniques.

Why not lower?

Every downward-pressure factor that normally applies to RCE vulns is absent here. There is no authentication barrier, no user interaction requirement, no unusual configuration prerequisite, and no network segmentation assumption — the default config is vulnerable and the attack surface is the entire internet-facing PHP fleet. Active exploitation is confirmed. A weaponized tool exists. The affected population is measured in millions of hosts. Any severity below CRITICAL would be negligent.

06 · Verification

Crowdsourced verification payload.

Run this script on each target host (or via your fleet management tool like Ansible/Salt/Puppet) as any user with read access to the PHP binary. Invoke: bash check_php2shell.sh — no arguments needed. The script checks all PHP binaries in PATH and common FPM locations, reports version and session.upload_progress.enabled status, and exits 2 (VULNERABLE), 0 (PATCHED), or 1 (UNKNOWN).

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_php2shell.sh — CVE-2026-109619 php2shell vulnerability checker
# Usage: bash check_php2shell.sh
# Exit codes: 0=PATCHED  1=UNKNOWN  2=VULNERABLE

set -euo pipefail

PATCHED_VERSIONS_82="8.2.35"
PATCHED_VERSIONS_83="8.3.36"
PATCHED_VERSIONS_84="8.4.27"
PATCHED_VERSIONS_85="8.5.12"

version_gte() {
  # Returns 0 if $1 >= $2 (semantic version compare)
  printf '%s\n%s' "$2" "$1" | sort -t. -k1,1n -k2,2n -k3,3n -C 2>/dev/null
}

check_php() {
  local php_bin="$1"
  local ver
  ver=$($php_bin -r 'echo PHP_VERSION;' 2>/dev/null) || return 1
  local major_minor
  major_minor=$(echo "$ver" | cut -d. -f1,2)
  local upload_progress
  upload_progress=$($php_bin -r 'echo ini_get("session.upload_progress.enabled") ? "On" : "Off";' 2>/dev/null || echo "unknown")

  echo "[*] Binary: $php_bin"
  echo "    Version: $ver"
  echo "    session.upload_progress.enabled: $upload_progress"

  case "$major_minor" in
    8.2) if version_gte "$ver" "$PATCHED_VERSIONS_82"; then echo "    Status: PATCHED"; return 0; fi ;;
    8.3) if version_gte "$ver" "$PATCHED_VERSIONS_83"; then echo "    Status: PATCHED"; return 0; fi ;;
    8.4) if version_gte "$ver" "$PATCHED_VERSIONS_84"; then echo "    Status: PATCHED"; return 0; fi ;;
    8.5) if version_gte "$ver" "$PATCHED_VERSIONS_85"; then echo "    Status: PATCHED"; return 0; fi ;;
    5.*|7.*|8.0|8.1)
      echo "    Status: VULNERABLE (EOL branch — no official fix available)"
      return 2
      ;;
  esac

  if [ "$upload_progress" = "Off" ]; then
    echo "    Status: VULNERABLE (unpatched) but upload_progress is disabled (CVE-2026-109619 mitigated)"
    return 2
  fi

  echo "    Status: VULNERABLE"
  return 2
}

FOUND=0
VULN=0
PHP_BINS=$(which php php-fpm php-cgi php8.2-fpm php8.3-fpm php8.4-fpm php8.5-fpm 2>/dev/null || true)
# Also check common paths
for p in /usr/bin/php /usr/sbin/php-fpm /usr/local/bin/php /usr/local/sbin/php-fpm; do
  if [ -x "$p" ] && ! echo "$PHP_BINS" | grep -qF "$p"; then
    PHP_BINS="$PHP_BINS $p"
  fi
done

if [ -z "$PHP_BINS" ]; then
  echo "[!] No PHP binaries found on this host."
  echo "UNKNOWN"
  exit 1
fi

for bin in $PHP_BINS; do
  [ -x "$bin" ] || continue
  FOUND=1
  echo "---"
  if ! check_php "$bin"; then
    VULN=1
  fi
done

echo "==="
if [ "$FOUND" -eq 0 ]; then
  echo "UNKNOWN"
  exit 1
elif [ "$VULN" -eq 1 ]; then
  echo "VULNERABLE"
  exit 2
else
  echo "PATCHED"
  exit 0
fi