Back to blog

PHP2Shell (CVE-2026-109618): How MSPs Can Triage Exposed Clients Fast

Published on Oct 26, 2026 10 min read

The PHP project shipped emergency fixes on Oct. 21 for two flaws that let an unauthenticated attacker run code on any PHP installation serving web requests. Amazon says it saw an exploitation attempt four days before disclosure. By release day, proof-of-concept code was circulating and attacks were under way.

For managed security providers, the patch is only part of the job. A provider running SOC services for hundreds of small organizations has to work out which clients run vulnerable PHP, which ones are already being hit, and which ones it can’t see well enough to know. This report pairs the week’s PHP2Shell news with a scenario walk-through of that triage.

What PHP2Shell is

PHP2Shell is the name for an exploit chain built on two vulnerabilities in the PHP interpreter. CVE-2026-109618 is a memory corruption bug triggered when PHP runs out of memory while request data is only partly initialized. CVE-2026-109619 is a use-after-free in which state left over from one request points to freed memory that a later request on the same worker can reuse. The PHP security team rates both 9.2 under CVSS v4.0.

Because the bugs sit in the interpreter, the application on top doesn’t matter. PHP says every installation serving web requests is at risk, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. It considers mod_php the highest risk, since a demonstrated exploit fires before any application code runs. Wiz Research reportedly demonstrated unauthenticated code execution against the latest WordPress container image.

“These findings rank among the most severe vulnerabilities discovered in PHP, and arguably in software history.” (Wiz, via Help Net Security)

Fixed versions

  • PHP 8.5: fixed in 8.5.12
  • PHP 8.4: fixed in 8.4.27
  • PHP 8.3: fixed in 8.3.36
  • PHP 8.2: fixed in 8.2.35
  • PHP 7.4 to 8.1: no official release; minimal source backports from the PHP security team
  • PHP 5.4 to 7.3: end of life; no fix from the PHP project

Timeline

  • Oct. 17: Amazon’s MadPot honeypot records a PHP2Shell exploitation attempt from an IP tied to a commercial anonymization network. Amazon says it’s unclear how the actor got exploit details.
  • Oct. 20: CVE identifiers circulate publicly. The PHP release, planned for Oct. 22, is moved up.
  • Oct. 21, 02:00 UTC: Amazon identifies the first of several proof-of-concept exploits. Attempts from anonymized infrastructure run until 08:00, with payloads varied enough to suggest multiple actors.
  • Oct. 21, 14:00 UTC: Embargo lifts and patches ship. More PoCs appear within hours. CISA issues an alert urging organizations to patch immediately. Cloudflare deploys initial WAF rules for all customers.
  • Oct. 22: Palo Alto Networks confirms all PAN-OS and Panorama versions are affected, with no fixed releases yet. Fortinet and Cisco publish advisories while they check 12 and 31 products, respectively.

Security researcher Silas Cutler roughly estimates that more than 1.3 million internet-exposed hosts may be vulnerable. W3Techs puts PHP at 69.3% of websites whose server-side language is known. Kevin Beaumont said “it’s one of the biggest vulns in years.”

Upgrading is the only complete fix for both CVEs, according to Amazon. Where patching is delayed, Amazon suggests blocking multipart file uploads after testing the effect on the application, and starting investigations from Oct. 17 instead of the disclosure date. The UK’s NCSC urges taking offline any system that can’t be patched right away. Amazon also argues that critical internet-facing flaws should be measured in hours, not the 7- or 30-day patch SLAs many organizations use.

Why MSPs feel it first

An MSP doesn’t run one PHP estate. It runs dozens or hundreds, each with its own web server, log format, EDR policy and change process. When a bug like PHP2Shell lands, the questions that decide the next few hours are which tenants are exposed, which are being attacked, and which ones the provider has no visibility into.

To show what that triage looks like in practice, Fleak built a scenario around a smaller, app-level PHP bug hitting an MSP with 277 tenants and a five-hour contractual containment window. The mechanics carry over to PHP2Shell with a few changes, covered further down.

About the scenario: the provider, its tenants and the vulnerable application are illustrative. Customer details are anonymized, Northgate Managed Security is a placeholder name, and DeskFlow stands in for a widely deployed PHP application. Fleak capabilities assumed: inline Sigma and custom rules, per-event evaluation, Fleak-managed lookup tables under 10MB that refresh within seconds, and rule actions that write to those tables and fire webhooks.

A detection rule that covered 110 of 277 tenants

Northgate runs SOC services for 277 tenants, most with 50 to 2,000 employees: regional credit unions, clinics, manufacturers, law firms and school districts. Contracts commit to containing a confirmed compromise within five hours, a figure its cyber insurance carriers pushed for.

The stack is common. CrowdStrike Falcon runs under Flight Control with 277 child CIDs, and about 40% of tenants run detect-only on servers because clients fear outages. Splunk Cloud holds one index per tenant, Tines handles automation, and NinjaOne patches. Cloudflare fronts 94 tenants. Web logs arrive from nginx, Apache and IIS in a dozen formats, and 9 tenants never onboarded web logs at all.

The provider’s PHP injection Sigma rule was written against Apache-style field names. It effectively ran on about 110 tenants, and nobody had a list of which 110.

Grid chart titled "Where the old Sigma rule actually ran," with one square per tenant: 110 tenants where the rule matches the log format (dark squares) and 167 where it silently misses (light squares).

Fleak had been running across all 277 tenants for four months. It stamps every event with a tenant_id at ingest and maps web logs to the OCSF HTTP Activity class, so one Sigma rule covers every format. Rule hits go to each tenant’s Splunk index. Everything else goes to per-tenant S3, unsampled, kept 30 days. When a log source changes format, parse health drops and Fleak proposes a new mapping for an analyst to approve; the company says the median time from format change to working detection is about 7 minutes with its pipeline and about 2 weeks without.

Diagram titled "How data moves": per-tenant sources (web logs, Falcon via FDR, firewall and DNS, Entra ID or Okta identity) flow into Fleak, which stamps tenant_id at ingest, maps events to OCSF classes, runs rules R1 to R6 against managed lookup tables, and tracks parse health per tenant and source. Output lands in Splunk tenant indexes (rule hits and hot-entity events), S3 per-tenant prefixes (everything else, 30-day retention), and Tines webhooks that enforce actions in Falcon, Cloudflare, and NinjaOne.

The core of the design is a set of shared lookup tables. tenant_assets mirrors the NinjaOne inventory: which host runs which app and version, and whether it faces the internet. tenant_coverage tracks whether each tenant’s log sources are healthy. hot_entities holds source ranges, hostnames and URL paths that rules have flagged, with a 24-hour expiry. tenant_triage holds each tenant’s bucket, and rules can only move a tenant up, from exposed to attacked to isolate. Moving one down is an analyst action with an audit trail.

Rules evaluate one event at a time and read memory from those tables. R1 is the provider’s existing Sigma rule. R2 flags any request to a path another tenant’s attacker already exploited. R3 flags a web server process spawning a shell. R4 flags a .php file executing from an upload directory. Fleak emits findings and webhooks; Tines, Falcon and NinjaOne enforce.

Diagram titled "How one tenant's alert arms the others": an injection request against /helpdesk/ajax.php at tenant 14 matches rule R1 (risk 70) and writes the source /24, the path, and a triage flag into a hot_entities lookup table with a 24-hour TTL. Eighteen minutes later, tenant 203 sees the same path from the same /24 with a double URL-encoded payload; Sigma misses it, but R2 matches path plus hot source (risk 85, attacked). At tenant 48, a fresh VPS range matches R2 on path only (risk 40) until the next R1 hit writes that range in.

Thursday, 21:40 to 02:20

By T+4h40, all 61 exposed tenants had been contained or mitigated: five isolated, 53 patched and three held behind a temporary block pending client change approval.

  • T+0, 21:40. R1 fires in tenant 14. A regional credit union’s DeskFlow 4.2.1 host receives an injection payload in the filter parameter. R1 records the source /24, the exploited path and tenant 14 as attacked. Tines pages the night lead.
  • T+10 min. Blast radius: 61 of 277. Tines queries tenant_assets for vulnerable, internet-facing DeskFlow and gets 61 tenants. The other 216 are out of scope. Of the 61, 12 have no healthy web log source and are marked blind: 9 never sent web logs and 3 went stale in the past month.
  • T+18 to T+38. Buckets fill on their own. Tenant 203 is hit with a double URL-encoded payload that slips past Sigma; R2 catches it on path plus hot source. At tenant 77, a manufacturer on detect-only, w3wp.exe spawns cmd.exe /c whoami and Falcon logs only a medium detection; R3 moves the tenant to isolate. Tenant 112 is detected only because a log format break three weeks earlier had been caught and fixed. At tenant 160, a webshell request to an attachments folder returns 200 and R4 fires.
  • T+45 min. Four buckets. 4 isolate, 17 attacked, 28 exposed and 12 blind. Tines has Falcon network-contain the isolate hosts and pushes Cloudflare or server-level blocks on the vulnerable endpoint for the rest.
Grid of 277 tenants, one square each, color-coded by triage bucket: 4 isolate, 17 attacked, 28 exposed, 12 blind, and 216 out of scope. Caption: rules fill the buckets while the night shift watches; tenant 203 got in through R2, tenant 77 through R3, and tenant 160 through R4.
  • By T+2h. The blind bucket finds tenant 231. Two analysts hunt the 12 blind tenants in Falcon over 30 days of process data. Eleven are clean. Tenant 231, a school district with no web logs, shows php-cgi.exe spawning cmd.exe six days earlier, before the advisory was public. It is contained.
  • T+4h40. Reports out, 20 minutes inside the window. NinjaOne patches in bucket order. Three clinics under a change freeze stay behind the temporary block. Each of the 61 tenants gets a report built from its own findings.
Grid of 277 tenants, one square each, showing the outcome: all 61 affected tenants contained or mitigated, with 5 isolated, 53 patched, and 3 clinics behind a temporary block while account managers chase change approval; 216 were out of scope. Reports go out at T+4h40.

In the week after, a search of 30 days of unsampled S3 data turns up probing in 7 tenants starting 9 days before the first alert, from a VPS range that never reappeared. A provider keeping 7 days of web logs would have missed it. Response sizes in the logs show two isolated tenants had DeskFlow ticket tables pulled, and both get breach-notification support.

What changes when the bug is in the interpreter

The scenario’s structure holds for PHP2Shell, but several pieces work differently.

  • The blast radius query changes. Scope is every internet-facing host running a vulnerable PHP version, regardless of app, plus appliances that bundle PHP, such as the PAN-OS versions Palo Alto Networks has confirmed. That only works if the asset inventory records PHP versions, which many RMM inventories don’t.
  • Query-string rules won’t see it. Amazon’s guidance points to POST requests containing PHP_SESSION_UPLOAD_PROGRESS in form uploads. Access logs rarely capture POST bodies, so the equivalent of R1 needs WAF body logging or app-level logs. Patchstack lists softer signs: unusual multipart requests followed by memory-limit errors or repeated 500s, oversized PHPSESSID cookies, and unexplained worker restarts.
  • Process rules carry more weight. A web server process spawning a shell (R3) or writing unexpected files is the signal that doesn’t depend on parsing the exploit. That makes detect-only server policies a bigger risk.
  • The hunt starts Oct. 17. Amazon advises investigating from the first observed attempt, not the disclosure date. That requires raw web logs and process telemetry going back at least that far.
  • Attacker IPs are shared. Amazon notes the IPs it saw belong to anonymization infrastructure and recommends investigating them rather than blocking outright. That is the same reason the scenario records residential sources at /24 level instead of whole ASNs.

Where the approach falls short

  • Blind tenants stay blind. Fleak can show a tenant has no usable web telemetry, which is what led to tenant 231, but it can’t produce the missing logs.
  • First requests land. Fleak reads logs after the fact, so every bucket move happens seconds after the event that caused it.
  • The asset table is load-bearing. A wrong inventory means a wrong blast radius at T+10.
  • Slow campaigns slip through. An attacker who touches each tenant once a day from fresh infrastructure never trips the shared tables. Risk aggregation in Splunk over days is the backstop.
  • Retro hunting needs retention. Thirty days of unsampled data found the earlier probing; seven days would not have.

Questions worth asking this week

These apply to MSSPs reviewing their own operations, and to organizations asking their provider how PHP2Shell is being handled.

  1. How many tenants can you prove are sending parseable web logs today?
  2. Which log formats were your web detection rules written for, and how many tenants do they actually run on?
  3. Does your asset inventory record PHP versions on internet-facing servers and appliances?
  4. When a rule fires in one tenant, how fast can you check whether the same attacker or exploit path is hitting the others?
  5. How many tenants run Falcon in detect-only on servers?
  6. How long do you keep raw web logs, are they sampled, and do they reach back to Oct. 17?
  7. How do you decide which tenants to patch first when dozens are exposed and the contract sets a containment window?

Sources

Bring us your messiest data.

30 minutes. Bring your messiest data source.