Today PHP shipped emergency releases for two unauthenticated remote code execution flaws in the interpreter itself, tracked as CVE-2026-109618 and CVE-2026-109619, both rated Critical at CVSS 9.2 and already nicknamed PHP2Shell. Fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35, and Patchstack reports that hosting partners were seeing live exploitation attempts before the release even went out.

What makes this one different is the layer it lives at. These are not WordPress bugs or Laravel bugs. They sit in request handling inside PHP, which means every application on a vulnerable interpreter is exposed, including the bare "Hello World" page Wiz used to prove it. PHP rates the risk highest for mod_php, where a hostile request can trigger the flaw before a single line of application code runs. Your input validation, your framework middleware, your carefully reviewed controllers: none of it is in the path yet.

The disclosure itself was messy. Details leaked on 20 October ahead of the coordinated release, so defenders spent the gap watching rather than patching. Dutch government services including DigiD and MijnOverheid went offline as a precaution, several large Japanese platforms entered emergency maintenance, and Cloudflare pushed initial WAF rules it openly calls a stopgap. That is the correct instinct on all sides: when the flaw is underneath the application, partial shields go up first and the real fix follows.

What the flaws do

CVE-2026-109618 is an out-of-memory handling fault: allocation failure can interrupt PHP while internal request data is only partly initialised, and cleanup then treats the invalid data as valid. CVE-2026-109619 is stale state: request-owned memory can survive after it is freed, so a later request handled by the same worker may reuse it before normal initialisation resets it. Wiz Research, which reported both flaws to PHP, says every PHP version back to 5.4 carries at least one of them, and demonstrated reading worker memory without achieving code execution. That last point widens the blast radius quietly: passwords, tokens and API keys sitting in that process are in scope even where the attacker never gets a shell.

The fix, and its edges

The fix is the new releases, full stop. Linux distributions and LTS vendors received the patches in advance, so distro packages should carry the fix now. Two edges matter. PHP 8.1 and older get no upstream fix at all: the project publishes only minimal source backports for 7.4, 8.0 and 8.1, so anything older lives or dies by its distro or LTS vendor, or it needs upgrading. The volunteer remirepo.net RPMs will not have updates today, and PHP says to switch those hosts to distribution packages or build from source. If a fleet mixes PHP sources, that split decides today's plan more than anything in the advisories.

The order that works

When the flaw is in the interpreter, the inventory is the patch. You cannot fix PHP installs you do not know about, and PHP hides: FPM pools, mod_php, CLI cron jobs, containers carrying their own interpreter, appliances and third-party software with PHP bundled inside. The first move across any fleet is a sweep that records version, SAPI and pool config per host, not a blind upgrade. One pass running php -v plus a package listing on every managed host tells you within minutes what needs patching, what is end of life, and what your automation cannot reach.

Then patch with a narrow playbook, not a full upgrade. A PHP fix needs new packages plus a worker restart, nothing more: no reboot, no unrelated package churn inside the same change window. Restart every FPM pool and Apache worker, assert the running version meets the fixed release for its branch, and keep the report. Test on one host first, widen in rings, and treat the internet-facing boxes as ring zero.

For what cannot be patched today, PHP publishes partial mitigations with unusual honesty about their limits. For the out-of-memory flaw: set memory_limit to -1 where applications cannot override it, keep post_max_size low, and prefer container or cgroup limits over RLIMIT_AS or RLIMIT_DATA, which can trigger the vulnerable path themselves. For the stale-state flaw: disable session.upload_progress and restart every worker, and on PHP 8.5 set fatal_error_backtraces to 0. Read the qualifiers in the advisories before you relax: each measure closes confirmed paths, none closes them all. A mitigation with documented holes is still worth deploying tonight, as long as nobody files it as done.

Finally, name what automation will not cover. Containers need rebuilt images, not package upgrades on the host. Bundled PHP inside appliances needs the vendor. End-of-life branches need the migration plan that should have started last year. The sweep from the first step should flag all three, so they become tracked exceptions instead of silent gaps.

Signs you were probed

The flaws act below the application layer, so application logs may show little. Wiz suggests watching for unusual multipart requests followed by memory-limit errors or repeated 500 responses, oddly large or malformed PHPSESSID cookies, and unexplained PHP worker restarts. These signs can guide an investigation but prove nothing either way. If you see them, update first and investigate second, then rotate the credentials those workers held, since memory disclosure sits inside this flaw's range.

What we do here

This site itself is one Ubuntu host: nginx terminating TLS, one PHP-FPM behind it, PostgreSQL and Keycloak beside it, and no third-party anything on a page load. The inventory fits in a paragraph, which is the entire strategy here. PHP arrives through the system package manager, so the fix comes down the normal update channel, and the job is packages plus a worker restart, verified rather than assumed. The wider estate we manage for others is where the same thinking has to run as automation rather than memory.

That estate lives in one Ansible inventory, and a day like today is what the playbooks are for. One sweep playbook records PHP version, SAPI and pool config per host, so the morning starts with a list instead of a hunt. One targeted patch playbook upgrades the PHP packages and restarts the workers, nothing else, no reboot, with the running version asserted branch by branch at the end. Test on one host, widen in rings, internet-facing first, and keep the report: one command to find PHP everywhere, one to patch it, and proof it happened. Containers and bundled interpreters still fall outside that net, so the sweep flags them as tracked exceptions rather than letting them stay silent gaps.

Verification is the part I would keep even at ten times the size. Every deploy carries its commit into the footer stamp and /api/version.php, so proving what is live takes one curl, and a push reaches production within about a minute when a code-side mitigation has to ship fast. The status page watches the site from the outside, daily backups hold uploads and the database, and mail sends are logged so a silently broken subsystem gets noticed this time. I checked our own host tonight: Ubuntu's noble-security backport patches both CVEs, it landed today, and every FPM worker postdates the install. None of that is sophisticated. It is just the unglamorous version of the runbook above, sized for estates that fit in one inventory.

The next step is Wegweiser, the scanner-side product I build for managed service providers. The plan is to put automatic intelligence in front of days like today: advisories matched against the estate inventory as they land, affected hosts surfacing themselves within minutes instead of waiting for someone to run the sweep by hand. Today's flow works because the inventory exists and the playbooks are rehearsed. Wegweiser should make the first half of that flow automatic, so the reaction starts when the advisory drops rather than when somebody reads it.

The uncomfortable lesson of PHP2Shell is how much of the web stands on one interpreter. A single coordinated release just touched WordPress, Drupal, Joomla, Magento, Laravel and everything else at once, which is also the argument for keeping that layer boring: distro packages, supported branches, an inventory you trust, and a tested path from advisory to restarted workers measured in hours. The next interpreter bug is a matter of time. The inventory is what decides whether it costs you an afternoon or a week.