Analysis
The Attack Arrived Before the Patch: PHP2Shell and the Question Patching Cannot Answer
CVE-2026-109618 and CVE-2026-109619 (CVSS v4.0 9.2) are unauthenticated remote code execution in PHP itself. Exploitation was observed roughly six hours before the fix shipped. Patching closes the door going forward; it tells you nothing about who already walked through it.
21 October 2026
The Vulnerability
On 21 October 2026 at 14:00 UTC, the PHP project released coordinated fixes for two critical vulnerabilities in the language runtime. CVE-2026-109618 is an upload-progress unserializer use-after-free permitting remote memory disclosure and code execution. CVE-2026-109619 is a fatal-error backtrace use-after-free leading to OPcache type confusion, also permitting code execution. The PHP security team rates both CVSS v4.0 9.2, Critical. Researchers at watchTowr argued publicly for 10.0. The pair has been nicknamed PHP2Shell.
Both bugs share a root cause. A fatal error during request handling, typically an out-of-memory condition when a request crosses the configured memory_limit, interrupts native PHP code before it has finished cleaning up. Request-owned internal state survives past the point its backing memory was freed. A later request, handled by the same worker process, reaches that stale state before normal initialization resets it, and treats invalid data as valid. From there an attacker controls the contents of freed memory, and from there, the process.
The practical reading is short. An unauthenticated attacker sends crafted requests to any URL on a PHP site, and gets code execution as the web server user. No credentials. No user interaction. No vulnerable plugin, theme, or application feature is required: the flaw is in PHP, not in anything built on top of it. Every documented exploit path to date uses multipart/form-data requests, which every PHP deployment on the internet accepts by default.
What makes this categorically worse than the usual critical CVE is when the code runs. The vulnerable path is in request setup. A single unauthenticated request triggers it before any application code executes. Your framework's router never runs. Your authentication middleware never runs. Your application's audit log never gets a chance to record anything, because the process was taken over before the application existed in it.
PHP2Shell At a Glance
- CVEs: CVE-2026-109618, CVE-2026-109619
- CVSS v4.0: 9.2 Critical (PHP security team); watchTowr assessed 10.0
- Impact: Unauthenticated remote code execution as the web server user
- Vulnerable code present since: PHP 5.4.0, released 2012
- Fixed in: PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 — released 21 October 2026, 14:00 UTC
- No fix expected: PHP 5.x through 8.1.x (end of life)
- Affected SAPIs: PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed. CLI is not a web-facing concern here.
- Trigger:
multipart/form-datarequests in every documented exploit - Exploited in the wild: Yes — Cloudflare reported attacks from 07:43 UTC on 21 October
- Confirmed affected applications include: WordPress, Drupal, Joomla, Magento, Laravel, Symfony, Nextcloud, Roundcube, phpMyAdmin, MediaWiki, WHMCS
The Timeline Is the Story
Read these two timestamps next to each other.
Cloudflare observed exploitation attempts beginning at 07:43 UTC on 21 October. The coordinated fix was published at 14:00 UTC the same day. Attacks preceded the patch by more than six hours, and CVE identifiers plus the PHP security team's advance notice had been circulating since 20 October. Cloudflare CTO Dane Knecht described the position plainly: early attacks emerging, mitigations being updated as new vectors appeared.
That ordering changes what patching means. In the ordinary critical-CVE sequence, a fix ships, exploitation follows days or weeks later, and the organizations that patch inside that window are genuinely fine. Here the window ran the other way. An organization with flawless vulnerability management — monitoring the advisory, patching within hours of release — may still have been exploited that morning, using an exploit that was already working while the fix was still embargoed.
Patching is necessary and urgent and should already be underway. It is also, on its own, an answer to the wrong question. It answers can we be exploited tomorrow. The question that determines whether this becomes an incident is were we exploited this morning, and no patch answers it.
Why Your Logs Look Clean
Most organizations will check for this the way they check for everything: search the SIEM for indicators, find nothing, and move on. The absence will be reassuring and it will not mean very much.
Consider what each layer of the stack actually recorded. The application logged nothing, because the exploit completes before application code runs. The web server access log recorded a POST to some ordinary URL with a multipart/form-data content type, indistinguishable at a glance from every file upload, contact form, and login submission your site handles. The WAF is doing real work here — Cloudflare shipped rules covering known exploit vectors to all customers on day one — but those rules target observed payload patterns for a memory-corruption bug with multiple trigger paths, and vendors were still adding vectors as they surfaced. The endpoint agent, if one runs on your web tier at all, saw a legitimate, expected, long-running php-fpm process continue to run.
And there is no published signature to search for. Across the major vendor writeups on disclosure day — Sansec, Tenable, Patchstack, Help Net Security — the consistent position is mitigation guidance without indicators of compromise, log signatures, or forensic triage steps. Code claiming to exploit the flaws circulated publicly almost immediately; verified detection content did not. Defenders were handed an emergency and no detection logic to go with it.
So the honest statement of the problem is this: the initial exploitation is close to invisible at the point it happens, and the only durable evidence is what the attacker did next.
What Post-Exploitation Actually Looks Like
Code execution as the web server user is the beginning of an intrusion, not the end of one. The behaviors worth hunting are the ones that follow:
- A web server process spawning a child it has no business spawning.
php-fpmorhttpdexecutingsh,bash,curl,wget,python, or a package manager. - New or modified files in the webroot written by the web server user — the classic webshell drop, often with an innocuous filename in a directory full of legitimate PHP.
- Outbound connections from the web tier to destinations it has never contacted. Web servers receive connections; a sudden egress pattern is a far stronger signal than anything in the inbound request.
- Reads of configuration files holding credentials —
.env,wp-config.php,app/etc/env.php,configuration.php— by a process or at a time that does not match normal application startup. - Database access from the application account that does not match application query patterns: bulk reads of user tables, credential tables, or payment records.
- Persistence established outside the webroot — a new cron entry, a systemd unit, an added SSH key, a new local account — so that patching PHP does not evict the attacker.
Every one of those is visible in telemetry most organizations already collect. Each one, in isolation, also has a boring explanation: a deploy script, a backup job, a developer, a scheduled import. That is precisely why they get closed.
The End-of-Life Problem
The vulnerable code has been in PHP since 5.4.0, released in 2012. Fourteen years of releases carry it. Fixes exist only for the supported branches: 8.5, 8.4, 8.3, and 8.2. Everything from PHP 5.x through 8.1.x is end of life and will not receive a patch.
The share of the web still serving requests on PHP 7.4 or earlier is not small, and it concentrates in exactly the places least equipped to respond: legacy e-commerce, hosted CMS installs nobody has owned since the agency contract ended, internal applications running on a VM whose last touch predates current IT leadership, and the long tail of small-business sites on shared hosting. For those systems the choice is a major version upgrade under emergency conditions, a compensating control, or taking the system off the internet — which is the course the NSA explicitly recommended where immediate updating is not feasible.
There is a second-order version of this problem that deserves a line of its own. The asset you cannot patch is usually the asset you did not know you had. Most organizations do not have a reliable inventory of every PHP version running on every host in their estate, including the marketing microsite, the acquired company's intranet, and the appliance with a PHP admin interface nobody thinks of as a web server. Step one of the response is a project in its own right, and the clock started before the patch did.
If You Cannot Patch Today
Published mitigations are explicitly partial and temporary, and none substitutes for upgrading. In rough order of effectiveness:
- Block or tightly restrict
multipart/form-datarequests at the web server or reverse proxy. Every documented exploit uses them. This breaks file uploads and some form submissions — on many sites that is an acceptable trade for a few hours. - Restrict exposed systems to GET and HEAD, or take them offline entirely, where the application can tolerate it.
- Ensure WAF rules are current and that you are receiving vendor updates as new vectors are published.
- Treat
session.upload_progress.enabledandmemory_limitadjustments as marginal. They have been circulated, and PHP core developers have been blunt that ini settings are not a fix and patching is the only real mitigation.
Apply what you can, then move straight to the harder work: determining whether any of it was applied too late.
What Actually Catches This
The detection problem here is not a missing rule. It is that the evidence is distributed across systems, across time, and across the boundary between "suspicious" and "ordinary" — and it only becomes legible when the pieces are assembled.
A POST with a multipart content type is not an alert. A web server spawning curl is a low-severity alert that fires constantly in environments with deploy automation. A new file in the webroot is a change-management event. An outbound connection from a DMZ host is an informational netflow record. A database read of the users table from the application account is normal by definition. Six signals, six plausible benign explanations, six tickets closed inside the triage window.
Threaded together — same host, same hour, starting from a multipart POST at 08:12 UTC on 21 October — they are an intrusion with a timestamp.
That assembly is what Intruex automates. Every alert is investigated by pulling in surrounding behavior across identity, network, endpoint, and application telemetry rather than being judged on its own contents. When a web server process spawns a shell, the investigation does not stop at "deploy script, probably." It asks what request that worker was handling, what the process did next, what it wrote to disk, where it connected, what it read, and whether any of that matches the host's established baseline — automatically, for every alert, rather than for the handful an analyst has time to pursue. Normalization onto OCSF is what makes the web server log, the netflow record, the process event, and the database audit line comparable in the first place. Median AI triage time was 48 seconds across 728 alerts in July 2026 (measured). No response action fires without human approval.
It is also what makes retrospective hunting tractable. "Did anything exploit PHP on our estate between 20 and 21 October" is not a question you answer with a signature search, because no reliable signature exists. It is a question you answer by examining behavior on every PHP-serving host across that window and asking which sequences do not fit — which is a volume problem, and volume is the part that does not require a human.
The Core Question
Your team will patch PHP this week. Assume they do it perfectly. Now answer the other question: if a worker process on one of your web servers was taken over at 08:00 UTC on 21 October, what in your environment would have noticed between then and now? If the answer depends on an analyst connecting a multipart POST, a shell spawn, a new file, and an outbound connection that arrived in four different consoles on four different days, then the patch closed the vulnerability and left the intrusion in place.
Sources: PHP project security advisories (21 October 2026); NSA Cybersecurity advisory on PHP updates; Sansec PHP2Shell research; Tenable CVE-2026-109618 / CVE-2026-109619 FAQ; Help Net Security; Patchstack; Cloudflare statements on observed exploitation
