跪拜 Guibai
← All articles
Frontend · Backend · PHP

php2shell: PHP Patches Two Unauthenticated RCEs with a Single Corrupted-Memory Root Cause

By iDao技术魔方 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

A single unauthenticated HTTP request can produce a shell on any unpatched PHP site, and Talos's estimate that half of public PHP installs are too old to ever receive a fix puts a large share of the web at permanent risk. The operational stakes are just as high: reloading FPM is not enough, and default mod_php configuration is a confirmed attack path.

Summary

PHP shipped 8.5.12, 8.4.27, 8.3.36, and 8.2.35 on October 21, 2026 to close two unauthenticated RCE vulnerabilities rated CVSS v4.0 9.2. Researchers named the pair php2shell because a single HTTP request can yield remote code execution before application code runs. CVE-2026-109618 is triggered through multipart uploads when an OOM abort via longjmp leaves half-initialized memory for cleanup routines; CVE-2026-109619 is a use-after-free in worker global state, confirmed via session.upload_progress and, on PHP 8.5, fatal error backtraces. Both share a single root cause: memory left damaged on an abnormal request boundary.

Exposure is broad. Wiz puts PHP behind roughly 70% of websites, Dataminr found at least two million public PHP instances on disclosure day, and Cisco Talos estimates about half of PHP sites run versions old enough that no official update exists. Branches 8.1 and earlier are EOL and get no patch. QiAnXin reported in-the-wild exploitation on October 22; Talos had not confirmed compromise as of October 21.

The upgrade itself is only half the job: verify CLI and FPM versions separately, restart rather than reload workers, confirm process start times, and re-check configuration after upgrading. For installs that cannot move off EOL branches, the recommended mitigation disables session.upload_progress, disables fatal error backtraces on PHP 8.5, and avoids RLIMIT_AS/RLIMIT_DATA as memory_limit substitutes. The disclosure timeline also shows AI-assisted auditing at work: three teams using frontier models converged on the same root cause within weeks.

Takeaways
— Both CVEs, 2026-109618 and 2026-109619, score CVSS v4.0 9.2 and enable unauthenticated RCE; patches landed in 8.5.12, 8.4.27, 8.3.36, and 8.2.35.
— CVE-2026-109618 is a multipart-upload trigger: PHP's longjmp OOM abort leaves half-initialized memory that request shutdown cleanup then frees; Apache mod_php default config is a confirmed unauthenticated path.
— CVE-2026-109619 is a use-after-free from stale worker globals; confirmed paths include session.upload_progress in mod_php and FPM, plus fatal error backtraces on PHP 8.5.
— Every branch since 5.4 is affected; 8.1 and earlier are EOL with no patch, so those installs need migration or isolation.
— Wiz estimates PHP runs about 70% of websites; Dataminr saw at least 2 million public PHP instances on disclosure day; Cisco Talos says roughly half of PHP sites will never upgrade because they are too old.
— Upgrading requires restarting, not reloading, FPM or Apache workers; reload only re-reads config and leaves old code in memory.
— Temporary mitigations are memory_limit -1 with post_max_size capped at 8M/32M, session.upload_progress.enabled=0, and fatal_error_backtraces=0 on PHP 8.5; do not substitute RLIMIT_AS/RLIMIT_DATA for memory_limit.
— Compromise indicators: rising segfaults and worker exits, memory spikes and response jitter, unexpected Set-Cookie content, unknown child processes like shell/curl/wget, unrecognized outbound connections, and files that were never deployed.
Conclusions

The PHP Foundation funded an external audit in 2024, core developers noticed OOM signals, and the issue was shelved because nobody had a concrete exploit path. A known bug without a known path is not a closed case; the foundation's own 'structural failure' postmortem treats the delay, not the original discovery, as the mistake.

Wiz's statement that frontier AI turned a long-standing flaw into a usable exploit is the sharpest claim here. Three independent teams converged on the same root cause within weeks of each other using AI-assisted tooling, while Cloudflare and watchTowr observed attackers doing the same kind of work on the offensive side; the same models lowered the cost of both finding and weaponizing this bug.

The two CVEs share a root cause that lives on error paths: longjmp during OOM and stale worker state after an abnormal request. C codebases typically test happy paths far harder than these boundaries, and PHP's case shows that cleanup logic running on half-initialized memory is exactly where critical RCEs hide.

Concepts & terms
php2shell
The name researchers gave to the two PHP RCE vulnerabilities disclosed in October 2026. It signals that an unauthenticated attacker can go from one HTTP request to a remote shell.
longjmp()
A C library function that performs a non-local jump. PHP uses it to abort a request when memory_limit is exceeded; if the abort lands on half-initialized request data, later cleanup can free or dereference invalid memory.
use-after-free
A memory-safety bug where a program accesses memory after it has been freed. In CVE-2026-109619, a worker process keeps global state pointing at memory from a previous request, and the next request touches that stale pointer before resetting it.
mod_php
The Apache module that embeds PHP directly into the web server process. Default mod_php configuration is a confirmed attack path for CVE-2026-109618.
session.upload_progress
A PHP feature that tracks file upload progress in session data. It is one of the confirmed exploitation paths for CVE-2026-109619.
CVSS v4.0
The fourth major version of the Common Vulnerability Scoring System. A score of 9.2 out of 10.0 puts these vulnerabilities at the critical end of the scale.
From the discussion

The disclosure gets a sarcastic salute for PHP's continued relevance, while the reality check is that legacy sites and open-source systems keep a huge attack surface alive. One operator reports cutting PHP's default timeout from 30 to 8 seconds and seeing failure rates drop, leaving the ideal production timeout unresolved. The writeup stops short of saying whether php2shell leads to a concrete shell, so the most obvious impact question hangs unanswered.

— A public RCE is a sharp reminder of how much of the web still runs on PHP, and legacy/open-source deployments keep that attack surface broad.
— Cutting PHP's default timeout from 30 to 8 seconds lowered failure rates in one production environment, but no settled answer emerges for what production should use.
— The writeup does not state whether php2shell can be leveraged for actual shell access, leaving the practical severity unconfirmed.
Featured comments
数字农民工

You only realize how powerful PHP is when a vulnerability like this comes out 👍

iDao技术魔方

Indeed, there are way too many old websites and assorted open-source systems.

AI向善

We cut PHP's default timeout from 30 seconds to 8 seconds, and the failure rate actually went down. What do you set for production?

爱上咖啡香

They didn't say whether this vulnerability can be exploited to get a shell.

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗