跳到正文
DEV Community· Mike Dabydeen·· 2 小時前精選AI 評分82

PHP php2shell:請求中途失敗仍可能留下狀態

php2shell: a request that fails halfway still leaves state behind

AI 導讀

php2shell 涉及兩項 PHP 執行環境漏洞,請求在初始化中途因致命錯誤停止後,可能令同一 worker 的後續請求讀取殘留狀態。PHP 安全團隊將 CVE-2026-109618 和 CVE-2026-109619 評為嚴重,評分為 CVSS v4.0 9.2;研究人員報告這些漏洞可導致記憶體資料洩露及遠端程式碼執行。

推薦理由

文章從 php2shell 的失敗路徑整理修補次序與緩解措施,並提出檢查重用 worker 在異常中止後狀態殘留的方法。

正文 · 原文

Upgrade the PHP runtime itself — to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or your vendor's backported package — and restart every worker. Updating WordPress, Drupal, Laravel or a plugin does not fix this one. The two flaws disclosed on 21 October, CVE-2026-109618 and CVE-2026-109619 ("php2shell"), live in the runtime, below your application code.

That is why several Government of Canada sites, the STM's site and the SAQ's site went offline on Wednesday as a precaution. Taking a site down until it is patched is a blunt control, but for a bug that can fire before any application code runs, it is a coherent one.

What follows is what I'd want a team to understand before they close the ticket: what failed, what the interim settings do and don't buy you, and the design lesson underneath.

What failed

The PHP security team rates both CVEs critical (CVSS v4.0 9.2). Its advisory describes two related failures:

  • CVE-2026-109618: out-of-memory handling can interrupt PHP while internal request data is only partially initialised, and request cleanup may then treat that data as valid.
  • CVE-2026-109619: request-owned state can survive after its memory has been freed, and a later request handled by the same worker may use it before normal initialisation resets it.

The shared pattern:

  1. A request hits a fatal condition part-way through setup — out-of-memory is the main example — and stops abnormally.
  2. Request-scoped state is left partially initialised, or still referenced after its memory has been freed.
  3. Cleanup, or the next request served by the same worker, trusts that state.

From there, researchers report memory disclosure and remote code execution with the web worker's privileges. Wiz says it demonstrated unauthenticated RCE against WordPress, Drupal and Joomla, and that a trivial "Hello World" script on an affected version can be exposed — the application does not have to do anything wrong.

The PHP team says every installation that serves web requests is at risk: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. It considers mod_php the highest risk, with a demonstrated exploit that triggers before application code runs.

The design lesson: request isolation is an assumption

PHP's long-standing appeal is the shared-nothing model: each request starts clean and everything is torn down at the end. Long-lived workers make that model an implementation promise rather than a physical fact. The process survives; the runtime is responsible for making each request behave as if it didn't.

php2shell is what it looks like when that promise breaks on the failure path. The happy path resets state. The path where a request dies halfway did not reset it completely, and the next request inherited the residue.

That pattern is not specific to PHP. Anywhere a worker, pool, connection or agent loop is reused, ask the same question: if this unit of work stops at the worst possible line, what does the next unit of work see? Cleanup that only runs on success is not cleanup. It is the same question that sits under retries and partial completion: recovery starts with knowing what state a failed step left behind.

What to do, in order

1. Inventory every PHP runtime, not just your apps. Huntress's list is a good start: web servers, vendor appliances, Windows software that bundles php.exe or php-cgi.exe, control panels such as cPanel and Plesk, container base images, and CMS hosts. php -v on the CLI is not proof of what FPM or mod_php is running; check the SAPI that actually serves traffic.

2. Patch internet-facing systems first, then restart. Fixed upstream releases: 8.5.12, 8.4.27, 8.3.36, 8.2.35. PHP published minimal source backports for 7.4, 8.0 and 8.1; those branches no longer receive upstream releases, so check your distribution or LTS vendor. A package upgrade that leaves old FPM workers running has not finished the job.

3. Treat interim settings as partial, because PHP says they are. The advisory lists mitigations while you schedule the upgrade, and states they do not fully protect:

; Interim only — PHP says these do not fully prevent exploitation.
; Restart all workers after changing them.
session.upload_progress.enabled = 0

; Prefer php_admin_value in the FPM pool / Apache config so apps can't override.
post_max_size = 8M        ; 32M if you genuinely accept uploads

; PHP 8.5 only: blocks the confirmed fatal-backtrace path, nothing more.
fatal_error_backtraces = 0

The advisory also suggests memory_limit=-1 paired with a container or cgroup limit that kills only the affected worker, and request limits at the web server or proxy (decoded size, field count, multipart part count, header count, nesting depth). Think about the memory change before applying it: you are trading PHP's own limit for an external one, so the external one has to exist and has to be tight. The advisory specifically warns against using RLIMIT_AS or RLIMIT_DATA, which can trigger the vulnerable allocation failure.

Set session.upload_progress.enabled in php.ini, the FPM pool or server config. The advisory says not to rely on ini_set() or .user.ini, because the setting has to apply before request parsing.

Hiding errors with display_errors, error_reporting or @ does not help. The issue is the state a fatal error leaves behind, not whether the message is printed.

4. Use a WAF as a layer, not a fix. Cloudflare deployed managed rules about nine hours before disclosure and reports seeing attack traffic and payload mutation against them, and it is explicit that customers still need to patch PHP. By Wednesday evening, watchTowr, Patchstack, Hadrian and others had reported exploitation attempts; watchTowr said none it saw had succeeded yet, and it didn't expect that to last. Edge rules buy time; they do not change the runtime.

5. Look for evidence, since application logs may show little. Because the bug sits below the application, your framework logs may be quiet. Signals the researchers suggest:

  • PHP workers (php-fpm, php-cgi, httpd, apache2) spawning sh, bash, python, curl, wget or nc
  • multipart requests followed by memory-limit errors; bursts of HTTP 500s
  • unusually large or malformed PHPSESSID cookies
  • unexplained worker churn
  • new files in the web root, new cron jobs or new accounts

If you find any of these on an unpatched host, isolate it and preserve logs before you rebuild.

The patch window is shorter now

Two details from the timeline matter for planning. First, Calif reports finding the first issue on 1 August using GPT-5.6 Sol, and Wiz found it independently on 19 August with its own tooling and frontier models; OpenAI and the PHP team both warn that the same capabilities help attackers build exploits. Second, a leak on 20 October pulled the coordinated release forward by a day.

I don't think the useful conclusion is alarm. It is operational: the time between "fix available" and "exploit attempts observed" was, by Cloudflare's account, close to zero this week. Teams that can answer where does PHP run, who owns it, and how fast can we restart it in an hour are in a different position from teams that discover the answer during the incident.

Exposure is also not the same as market share. The widely quoted "70 per cent" figure is PHP's share of websites, not a count of exploitable hosts; Palo Alto Networks' initial estimate, as reported by Fortune, was 22.6 million potentially vulnerable instances. Exploit reliability varies with version, configuration and memory layout. Patch anyway — but plan from your own inventory, not the headline.

Sources

Facts checked against the PHP advisory and the sources above on the evening of 21 October 2026. I'll update this post if the guidance changes.

來源:DEV Community · dev.to