At 14:00 UTC on 21 October 2026 the PHP project shipped 8.5.12, 8.4.27, 8.3.36 and 8.2.35. The four releases carry the same two fixes, both rated CVSS v4.0 9.2, both allowing unauthenticated remote code execution. The advisory states plainly that there are no complete mitigations and that updating is the only safe choice.
The schedule moved a day early. On 20 October a Hacker News post described two PHP RCE flaws said to affect every version since PHP 5, rated 10.0 by the poster; watchTowr confirmed the underlying reports were real, and the PHP security team published an advance notice at 19:33 UTC with the release set for the following afternoon, a day before the date in the leak. A screenshot of an exploit tool named PHP2Shell, credited to Calif.IO, circulated alongside it, with a banner claiming coverage of every release from 5.4.0 onward. The tool, the 10.0 score and the 5.4 scope are all unverified; the official advisories name none of them.
What separates this pair from the ordinary PHP CVE is where the code runs. In the highest-risk configuration, Apache with mod_php, the project says "an unauthenticated request can trigger the attack before any application code runs." A framework update, a WAF rule on request parameters, or a guard inside the application sits downstream of that point.

The fixes
The patched versions are 8.2.35, 8.3.36, 8.4.27 and 8.5.12, for the four branches PHP still supports. Every earlier release on those branches is affected: the advisories list affected ranges of below 8.2.35, below 8.3.36, below 8.4.27 and below 8.5.12, with no configuration precondition attached.
PHP 8.1 and older get no release from the project. The last published builds on those branches remain 8.1.34, 8.0.30 and 7.4.33, and the end-of-life page lists them as unsupported. Because of the severity, the security team did publish minimal source backports as protected branches on php-src, named PHP-7.4-CVE-2026-109618-109619, PHP-8.0-CVE-2026-109618-109619 and PHP-8.1-CVE-2026-109618-109619, each carrying a single commit on top of the branch's final release. Those branches are a build-from-source route, not an installable package, and the advisory points at them for people building from source or without an LTS vendor. Distributions and commercial LTS vendors received the patches in advance, so their packages are the fastest route on those branches. The volunteer repository at rpms.remirepo.net will not have updates on release day, and the advisory tells users of it to switch to distribution packages or build from source.
OOM corruption
CVE-2026-109618 (GHSA-w8hw-w9fx-j79w) is a use-after-free born out of how PHP handles running out of memory. PHP uses longjmp() to abandon the current context on an OOM error. In some paths, request memory is temporarily reachable before it has been fully initialized, and if an allocation fails at that moment, the early exit is not accounted for and the request memory stays in an invalid state. The failed allocation does not have to be the invalid memory; the request shutdown procedure then reads or branches on garbage. The patch removes longjmp() from that path, replacing it with a delayed soft OOM at the next VM safe point and an immediate hard OOM through _exit() once a second, higher threshold is crossed.
Reaching OOM while staying inside the configured limits is the work of the multipart parser, and the advisory lists three routes. A multipart request without a usable declared content length can pass the initial post_max_size check and keep supplying body data. A single part can carry an unlimited number of headers, whose in-memory representation is much larger than their wire form, so a body inside post_max_size can exhaust a normal memory_limit. Empty file controls do not count toward max_file_uploads but still allocate the file metadata arrays, and nested field names multiply that work. Application-specific paths, where attacker input causes one large allocation or sustained memory pressure, do the same.
The SAPI differences decide whether the bug is reachable without any application involvement. Apache with mod_php calls the request shutdown procedure unconditionally, even when request startup has failed, so automatic body parsing can exhaust memory and hand invalid state to shutdown before the first line of PHP runs. The advisory describes complete RCE validated over HTTP against mod_php with default input and memory limits on a normal prefork setup with multiple workers. FPM exits the child when startup fails, which removes that exact sequence, but the source analysis still supports FPM as susceptible, and a validated FPM path exists for the second bug. FrankenPHP looks like mod_php in calling shutdown unconditionally, and LSAPI models FPM.
Stale worker state
CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp) keeps a worker alive between requests and lets state from one request survive into the next. PHP holds error, session, object and extension state in worker globals that point at request-owned memory, and those pointers must be reset before the request heap is released. A fatal error can abandon the operation before it performs the matching reset; a warning or other diagnostic during shutdown can repopulate the state after it was cleared. The heap is then freed while the global still points into it.
Exploitation needs a producer and a consumer in two requests handled by the same worker. The producer publishes request-owned state somewhere globally reachable; the consumer reads, frees or branches on it before normal initialization has replaced it. The document lists ordinary interruptions as producers: parse and compile failures, uncaught errors, a failed require, Maximum execution time exceeded, and OOM. The confirmed consumers are less obvious. One is INI diagnostics raised while applying configuration before request initialization, which happens with deprecated or invalid settings such as auto_detect_line_endings=1, a non-default filter.default, legacy assert.* or session directives, report_memleaks=0 on 8.5, a non-zero intl.error_level, or a malformed memory_limit. The other is session upload progress, which reaches session handling before ordinary initialization, is enabled by default, and needs no call to session_start().
Two properties make this one hard to reason about from the outside. The impact is stated as unauthenticated heap use-after-free, memory disclosure and remote code execution with the worker's privileges, and the confirmed RCE was demonstrated through the fatal-error backtrace path on PHP 8.5 with fatal_error_backtraces enabled, plus session lifecycle RCE validated against both mod_php and FPM. And hiding diagnostics does not help: PHP reads the global state before it decides whether to report, display or log the warning, so error_reporting, @ and display_errors offer no protection.
Exposure by SAPI and version
mod_php is the configuration to worry about first, because the attack needs no application code and no authenticated session. FPM is exposed conditionally: its pool settings are applied when the worker starts and .user.ini is applied after normal initialization, so the early consumer appears when a frontend such as nginx or mod_proxy_fcgi passes PHP_VALUE or PHP_ADMIN_VALUE settings that produce a diagnostic before request initialization. LSAPI can apply special environment INI values early and is a conditional candidate. FrankenPHP did not show an equivalent early configuration consumer in its default setup, which the advisory is careful to say does not make it immune.
The population behind those configurations is large. W3Techs counts PHP on 69.8% of websites whose server-side language it can identify, with 64.4% of those sites on PHP 8 and 27.7% still on PHP 7. Mallory counted about 884,000 internet-facing hosts whose HTTP banner reported PHP 8.1 or older to Shodan on 20 October, 59% of the roughly 1.5 million hosts reporting any PHP version, plus another 740,000 that reported PHP without a version. Those are banner counts rather than confirmed vulnerable systems, and the real exposure sits somewhere inside the larger group.
The commercial surface is what turns a runtime bug into an incident. Magento, Shopware and WooCommerce run on PHP and process payment data, which is the incentive structure behind card-skimming campaigns. Control panels such as cPanel and Plesk ship their own PHP builds, and so do appliances and Windows software bundles, which is why inventory is the part of this that takes longer than the patch.
Update path and partial mitigations
Updating is the whole fix. On a supported branch, take the vendor package where one exists, because distributions had the patches in advance; otherwise build from the tagged release. On 8.1 or older, upgrade the branch, or move the host off the public internet until a backport lands, which is the position CERT-EU took when it advised isolating internet-facing applications rather than waiting.
The configuration workarounds are partial. For CVE-2026-109618, setting memory_limit=-1 administratively removes the controllable per-request limit that the confirmed exploit depends on, but an allocation failure from mmap() can still hit the vulnerable path, and every worker must be restarted with no pool, directory or application setting restoring a finite limit. A low post_max_size (8M where uploads are not needed, 32M where they are, with upload_max_filesize below it) shrinks the room to build a large multipart body, and the frontend should enforce decoded request size, field count, multipart part count, header count and nesting depth, including for requests without a usable Content-Length. A container or cgroup memory limit can act as an availability boundary if it kills only the affected worker; RLIMIT_AS and RLIMIT_DATA are the wrong tool, because they make mmap() fail at the limit and drive PHP into the vulnerable path.
For CVE-2026-109619, session.upload_progress.enabled=0 and, on 8.5, fatal_error_backtraces=0 each close one confirmed path, applied in php.ini, an FPM pool or server configuration and followed by a restart of every worker. ini_set() and .user.ini are too late. Neither setting covers the other state families, and the advisory is explicit that no configuration mitigation covers every operation that can publish stale state. pm.max_requests=1 breaks the producer-consumer sequence at the cost of performance.
Detection has to assume the request never reaches the application. On Linux, a process monitor that logs every program a PHP worker starts will catch a shell, an interpreter or a download tool launched by php-fpm, php-cgi, httpd or apache2; Florian Roth's process-watcher rules for this event are the ready-made version, and they see the process only after it has started. For post-incident work the checklist is the standard one: preserve web server and PHP logs, then look for web shells, new files in the document root, new cron entries, scheduled tasks and accounts.
Impact projection
Sansec expects mass attacks within 15 minutes of the release, which is a vendor estimate rather than an observation, and the closest precedent supports the shape of it: when CVE-2024-4577 became public in June 2024, Akamai saw exploit attempts against honeypots within 24 hours. This pair is harder to weaponise than that one. Attack complexity is rated High with attack requirements present, success depends on the PHP build, loaded extensions, frontend behaviour and allocator layout, and multiple workers reduce the chance that the consumer request lands on the worker holding the stale state. The result is a retry loop rather than a single request, which raises the noise a defender can see and lowers the yield per attempt.
The blast radius is set by the worker account. On a shared host running mod_php, that is the account the web server runs as, and a successful shell inherits whatever that account can read, including other sites' document roots and configuration files with database credentials. On FPM with a pool per user, the damage stops closer to one site, which is one reason the FPM path is described as conditional rather than impossible. Either way the follow-on is familiar: web shells, skimming scripts injected into checkout pages, session and credential theft, miners, and an initial foothold that ransomware operators buy.
My expectation is that the patch curve will be worse than the disclosure curve. The fixed releases exist for four branches, but a meaningful share of the installed base is on 8.1 or older and depends on a distribution, a panel vendor or a container image maintainer to ship a backport, and the remirepo gap shows how thin some of those paths are. Container base images are the quiet case: a rebuilt application does not update the PHP layer unless the base tag moved. I would treat any internet-facing mod_php host that has not been restarted on a patched build as compromised until logs say otherwise, and I would expect CISA to add both identifiers to the KEV catalog once exploitation appears.
The longer-term effect is likely to be a change in where defenders put controls. A runtime flaw that executes before application code makes the PHP layer itself part of the attack surface, which means body parsing, multipart limits and per-worker lifecycle settings move from performance tuning into the security baseline, and a WAF that inspects only parameters is not covering this class of bug.
PHP 8.2 reaches end of life on 31 December 2026, about ten weeks after this release, so the 8.2.35 fix has a short support window and the next vulnerability on that branch will not get one.
References
- Security advisory: critical remote code execution vulnerabilities, update now (php.net)
- GHSA-w8hw-w9fx-j79w: RCE via OOM corruption (php-src)
- GHSA-6wmf-4rv5-p3rp: RCE via stale global state (php-src)
- PHP supported versions
- CERT-EU Security Advisory 2026-018: critical vulnerabilities in PHP
- Critical PHP RCE: CVE-2026-109618 and CVE-2026-109619 (Sansec)
- PHP usage statistics (W3Techs)
- Source backport branch for PHP 7.4 (php-src)
Loading comments…