On Tuesday 21 October 2026, the PHP project did something it reserves for genuine emergencies: the day before, it pre-announced a security release for a fixed date and time — 21 October, 14:00 UTC — and shipped on schedule, for every supported branch at once. PHP does not do that for ordinary bugs. Two vulnerabilities — CVE-2026-109618 and CVE-2026-109619 — allow unauthenticated remote code execution. Both are rated 9.2 out of 10 (critical) on the CVSS v4.0 scale. Both are, in the words of the advisory, being actively exploited in the wild.
If that sentence doesn't ruin your coffee, read it again slowly: anyone on the internet can run arbitrary code on your server by sending it a crafted HTTP request. No account. No password. No click.
This post is the long version of the advisory we sent our customers — what actually broke, why it is worse than the average CVE, and the exact sequence of commands that takes you from vulnerable to verified-patched in about twenty minutes.
Short on time? Jump to the fix. But read the restart warning on the way — it is the step everyone skips.
What "unauthenticated RCE" actually means
Security bulletins throw the phrase around, so it is worth being concrete about what happens when one of these bugs fires:
- An attacker sends a normal-looking HTTP request to any PHP endpoint on
your server — a WordPress login page, an API route, even a static-looking
URL that eventually hits
index.php. - The request triggers the vulnerable code path and the interpreter's internal state corrupts in a way the attacker controls.
- The attacker executes code as the PHP process — typically
www-data,apacheor the FPM pool user.
From there, the game is over fast. That user can read your application's database credentials from its config file, dump the database, install a web shell, add a persistent cron job, and use the box to attack the rest of your network. Attackers automate this end-to-end: exploit, drop implant, move on. The whole sequence takes seconds, and it scales to thousands of scans per hour.
The two bugs
The project advisory has the authoritative technical detail; here is what matters for operators.
CVE-2026-109618 — RCE via OOM corruption (GHSA-w8hw-w9fx-j79w). Out-of-memory handling can interrupt PHP while internal request data is only partially initialized — and the request-cleanup path may then treat that invalid data as valid. The lesson generalises, and it is worth keeping: error-handling paths are code too, and they run precisely when the system is least able to defend itself.
CVE-2026-109619 — RCE via stale global state (GHSA-6wmf-4rv5-p3rp). Request-owned internal state can survive after its backing memory has been freed. A later request handled by the same worker may then use that stale state before normal initialization resets it. PHP's request model is "shared-nothing" — each request starts with a clean interpreter — and this bug breaks exactly that isolation, which is the design feature that has saved PHP from whole bug classes for decades.
Both bugs are reachable over the network, need no authentication and no user interaction — exactly the combination that produces a 9.2.
Why mod_php servers are in the most danger
Every PHP deployment that answers web requests is affected, in all its modern flavours:
- PHP-FPM — the standard behind nginx, Apache and Caddy
- Apache with mod_php — the riskiest setup
- php-cgi — still around on legacy stacks
- FrankenPHP — the new app-server flavour
- LiteSpeed / OpenLiteSpeed
The mod_php case deserves emphasis. With FPM, PHP runs in separate worker processes, so memory corruption is at least contained inside the pool. With mod_php, the interpreter is compiled into the Apache process itself — and the advisory confirms a demonstrated exploit where an unauthenticated request can trigger the attack before any application code runs. A corruption bug in that setup strikes the web server directly, in-process, often with broader filesystem access than an isolated pool. If you still run mod_php in 2026, consider this CVE your migration budget.
And a subtlety that catches people on shared hosting and panels (cPanel, Plesk, CyberPanel): servers routinely run several PHP versions side by side. Updating 8.3 does nothing for the 8.1 pool still serving three old sites. You have to hunt down every version on the box — more on that below.
The fix: twenty minutes, end to end
1. Snapshot first. Take a VM snapshot or at least back up document roots and databases. A security update is still a change, and changes deserve a way back. Two minutes now saves the blame game later.
2. Update the packages.
# Debian / Ubuntu
apt update && apt upgrade php-*
# AlmaLinux / Rocky / RHEL
dnf update php-*
On panels: cPanel/WHM users run /scripts/upcp (EasyApache 4 builds are
already available), Plesk Obsidian updates from the panel, CloudLinux users
should watch the CloudLinux status page for Alt-PHP builds. Distribution
packages beat third-party repositories here — the volunteer-run remirepo
does not publish fixes on release day, and the PHP project itself
recommends distro packages or building from source.
3. Restart the web stack. The step everyone skips:
systemctl restart php-fpm
systemctl restart apache2 # and/or nginx
An update without a restart changes nothing. PHP-FPM workers and Apache's mod_php keep the old interpreter in memory until they are recycled. Your package manager will proudly report the new version while the process serving the internet still runs the vulnerable one. This is the single most common way "we patched it" turns out to be false in a post-incident review.
4. Verify what is actually running — the CLI and the web SAPI, and every installed version:
php -v
Then drop a two-line probe behind your vhost and load it over HTTP:
<?php phpinfo();
If the phpinfo() version does not match the CLI, you found a pool that was
never restarted — go back to step 3. On cPanel, check every ea-php
version in WHM's MultiPHP Manager; on Plesk, the panel's PHP versions page.
Do this per version, not per server.
Two audiences often forgotten: anyone running PHP 8.6.0 release candidates should move to RC4 — published the same day with these fixes; earlier RC builds are vulnerable. And on PHP 8.1 and older there is nothing to update to beyond minimal source backports for 7.4–8.1 — your path is a supported branch or a vendor that backports for you.
Can't patch right now? Honest interim measures
Sometimes the change window is Tuesday and the fix is Friday. The advisory is unusually concrete about what reduces exposure — and unusually blunt that only an update is a complete solution:
Against CVE-2026-109618 (OOM corruption):
- Set
memory_limit = -1, preferably where applications cannot override it (php_admin_valuein the FPM pool or Apache config) — and audit every pool, directory and app for a finite limit sneaking back in. - Lower
post_max_size: 8M for apps without file uploads, 32M if uploads are needed, withupload_max_filesizebelow it. Enforce limits at the web server or proxy too: decoded request size, input-field count, multipart part count, header count, nesting depth — including requests without a usableContent-Length. - Use a container or cgroup limit as an availability boundary that kills
only the affected worker. Do not substitute
RLIMIT_ASorRLIMIT_DATA— they can trigger the vulnerable allocation failure themselves.
Against CVE-2026-109619 (stale global state):
- Set
session.upload_progress.enabled = 0and restart every worker — viaphp.inior pool/server configuration that applies before request parsing, notini_set()or FPM.user.ini. This blocks the confirmed session paths, not every variant. - On PHP 8.5, set
fatal_error_backtraces = 0and restart every worker (blocks the confirmed fatal-backtrace path). - Remove per-request settings that emit diagnostics before initialization
— hiding diagnostics with
error_reporting,display_errorsor@does not help. - Fix attacker-reachable fatal errors and execution timeouts, including failures in callbacks and upload/import processing.
Treat all of it as a tourniquet: it reduces how easily an attacker can reach the bug; it does not remove the bug. Every hour of mitigation should be an hour spent testing the real upgrade.
How to know if someone got there first
Because exploitation is already active, patching is only half the job — the other half is asking "were we hit?" Do this even on servers you believe were patched quickly:
- Web server logs: unusual POSTs to odd endpoints, bursts of 500s from a single IP or ASN, requests with malformed or truncated bodies.
- Filesystem: files in web roots you never deployed — especially small
PHP files with encoded payloads (look for
eval,assert,gzinflate, long base64 strings), and.htaccessor config files changed recently. - Persistence: new cron jobs (
crontab -lfor every user, plus/etc/cron*), unknown systemd units, unexpected SSH keys inauthorized_keys. - Behaviour: processes you don't recognise, sustained outbound traffic that isn't yours, CPU spikes with no traffic to match.
- Compare with backup: a diff of the document root against last week's backup finds implants faster than any scanner.
If you find something: isolate the host from the network before cleaning. A compromised server is evidence, not just an outage.
The boring part that prevents the next emergency
Once the adrenaline fades, do the unglamorous work that makes the next advisory a non-event:
- Automatic security updates.
unattended-upgrades,dnf-automatic, or your panel's toggle — pick one, turn it on, and let security updates flow without a human in the loop. - A supported PHP. PHP 8.2 ends on 31 December 2026. Everything older than 8.2 is already unsupported, surviving only on vendor backports. The CVEs that land on dead branches do not get fixes — they get eulogies.
- Less surface. Remove PHP versions and applications you no longer use. Every interpreter on the disk is a door you have to watch.
- Defence in depth. A WAF, IP allow-listing on admin interfaces, MFA everywhere it fits. No single control should be load-bearing.
The bottom line
Two 9.2-rated bugs, exploits already in the wild, fixes already packaged.
The distance between "vulnerable" and "verified patched" this time is one
upgrade, one restart, and one honest phpinfo() per version on the server —
about twenty minutes of work for a night's sleep. Do it now. Then automate
it, so the next time PHP ships an emergency fix, the only thing you have to
write is the calendar invite for the upgrade you already planned.
Sources: PHP.net — security advisory of 21 October 2026, GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp. Fixed versions: 8.5.12, 8.4.27, 8.3.36, 8.2.35 (last 8.2 release before its 31 December 2026 end of life).
