claude-news
newsInfoQ·26 Oct 2026·Steef-Jan Wiggers

PHP Patches Two Critical RCE Flaws That Were Already Under Attack

PHP shipped 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to fix two critical unauthenticated remote code execution bugs; Amazon saw exploitation attempts four days before the patches.

Extract

PHP released 8.5.12, 8.4.27, 8.3.36, and 8.2.35 to fix two critical vulnerabilities allowing unauthenticated remote code execution. Amazon recorded an exploitation attempt four days before the patches shipped, and operators spent the day working out whether they ran PHP, where to get the package, and how to tell it had landed.

By Steef-Jan Wiggers

Explained

What happened

On 21 October 2026 the PHP project shipped 8.5.12, 8.4.27, 8.3.36 and 8.2.35 as security releases. A pre-announcement a day earlier rated both bugs CVSS v4.0 9.2. Amazon saw an exploitation attempt about four days before the patches shipped, so this was effectively a zero-day.

  1. CVE-2026-109618 (OOM corruption). When PHP runs out of memory, the out-of-memory handler can interrupt the engine while some internal request data is only half initialized. Request cleanup then walks that data as if it were valid.
  2. CVE-2026-109619 (stale global state). Some request-owned state can outlive the memory behind it. The next request handled by the same worker process can read that stale state before normal initialization resets it.
  3. Affected setups: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The PHP team calls mod_php the highest risk, with a working exploit where an unauthenticated request triggers the bug before any application code runs.
  4. A separate, lower-risk session upload progress bug (CVE-2026-115560) followed on 23 October. It needs an unusual runtime switch of session.serialize_handler and will be fixed in the regular cycle.

Operators spent the day on basic questions: do we run PHP anywhere, which package carries the fix, and how do we prove the new build is live.

Why it works

Both bugs come from one design choice: long-lived worker processes reuse memory across requests. PHP was built around a share-nothing model where each request starts clean. In practice, FPM and mod_php keep a worker alive for thousands of requests and rely on cleanup code to reset global state. If cleanup runs on corrupted data (bug 1) or skips a pointer to freed memory (bug 2), one request can shape memory that the next one trusts. Since the attacker controls request size and upload behavior, they can push the worker into the out-of-memory path on purpose.

The bigger pattern

This is the classic use-after-free across request boundaries problem, the same family as cross-request state leaks in thread pools, connection pools and pre-forked servers. The known part: error paths (OOM, timeouts, aborts) are the least tested code and the most likely to leave invariants broken. The new part: the trigger sits in request startup and upload handling, before routing or auth, so no framework or WAF rule in the app can stop it.

What it means in practice

  • Inventory first. Find every PHP runtime, including base images, sidecars, WordPress hosts and vendor appliances. Lack of inventory was the main delay here.
  • Verify the patch landed. Check php -v inside the running container or worker, not the package manifest, and restart FPM pools so old workers die.
  • If you cannot patch, apply the interim mitigations in server config, not ini_set() or .user.ini:
session.upload_progress.enabled = 0
post_max_size = 8M
; PHP 8.5 only
fatal_error_backtraces = 0
  • Cap request body size at the proxy, and use container or cgroup memory limits instead of RLIMIT_AS.
  • The general lesson applies to any pooled worker (Sidekiq threads, Kafka consumers, GPU inference workers): treat per-request state reset as a security boundary, test the OOM and abort paths, and consider recycling workers after N requests.
  • 8.2 reaches end of life on 31 December 2026, so plan the upgrade now.