Revenir au blog

Suivre et s’abonner

How Fastly Detected and Built a Coordinated Response to PHP2Shell

PHP2Shell Deep Dive

Simran Khalsa
Simran KhalsaChercheur en sécurité

PHP's coordinated release for CVE-2026-109618 and CVE-2026-109619 was scheduled for 14:00 UTC on October 21. In our Next-Gen WAF telemetry, tools named after the bug were scanning more than nine hours before that. By the time the release went out, more than 100 source IPs had used PHP2Shell-branded tooling under more than two dozen tool names. One of those tools was an exploit kit with the CVE number in its User-Agent.

This post covers what that early traffic looked like, how to tell real PHP2Shell traffic from two look-alike campaigns that are running at the same time, and what to do for applications in your business running PHP.

Anatomy of the Bugs

PHP2Shell is the nickname for two critical PHP vulnerabilities. Both are rated CVSS 9.2, and can allow unauthenticated remote code execution:

  • CVE-2026-109618: An out-of-memory condition can interrupt PHP while internal request data is only partly set up, and request cleanup then trusts that broken data.

  • CVE-2026-109619: Internal state can outlive the memory behind it, and a later request on the same worker picks it up.

For anyone trying to detect PHP2shell, it’s important to note that there's no payload string to match. The exploit is a condition: make the request fail at exactly the wrong moment. 

PHP says every installation serving web requests is at risk, including PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed. It rates mod_php highest. 

The fixed releases are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP 8.1 and older get no upstream fix.

The Timeline

Date/Time (UTC)

Event

Oct 20, ~15:00

The CVE IDs start circulating on forums and social media, with a wrong release time and a wrong CVSS score of 10.0. No technical details leaked. (PHP Foundation)

Oct 20, 19:30

PHP publishes its pre-announcement and moves the release forward a day, to Oct 21 14:00 UTC. (PHP Foundation)

Oct 20, ~20:00

Calif mistakenly publishes a video of its proof of concept working against several targets. The video is deleted; copies circulate. (PHP Foundation)

Oct 20, ~23:00

Mitigation advice shared with a few critical infrastructure providers may have leaked. (PHP Foundation)

Oct 20 00:00 to Oct 21 03:59

We saw no PHP2shell-named tools in our telemetry: about 13 hours of quiet after the CVE IDs surfaced. (Fastly telemetry)

Oct 21, 03:59

First request from a PHP2shell-named tool in our telemetry. (Fastly telemetry)

Oct 21, 05:11

First request from the most widespread exploit kit (User-Agent PHP2 shell-109618, boundary B7kQpoc618). (Fastly telemetry)

Oct 21, 07:41

Chainguard publishes built images containing the fix, more than six hours before the coordinated release. (PHP Foundation)

Oct 21, 13:34

GitHub security advisories published for both CVES. (GitHub)

Oct 21, 14:00

Coordinated release (scheduled): PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. By then, more than 100 source IPs had used PHP2shell-named tools. (PHP Foundation)

How the disclosure unfolded, and when PHP2shell tooling appeared. All times UTC. Disclosure events are as described in the PHP Foundation's postmortem and GitHub's advisory records.

The IDs for the PHP2shell CVEs (2026-109618 & 109619) leaked without technical details, and PHP2shell-named tooling showed up in our telemetry roughly eight hours after the exploit video. Both the first named tool and the first exploit kit we saw came before the early release of patched images. We won’t speculate what prompted any particular tool, but by the time the coordinated release went out, the scanning was well under way.

The Haystack

From the first PHP2shell-named tool through midday on October 22, we stored about 250,000 requests from tools with PHP2shell in the name, from roughly 3,200 public IP addresses.

Of course, not all scanners operate with attack intent. About a third of that traffic identifies itself as defensive: "authorized exposure check", "patch-state check", "non-exploiting". Plenty of organizations were checking their own environments, and that's a good thing. Self-labels are claims without proof but they line up with what those requests do: small, harmless probes.

Requests from PHP2shell-named tools, by tool type (Oct 21 03:59 UTC to midday Oct 22, rounded). 

The rest is a mix, and it moved fast. One PHP version-inventory tool went from version 4.0 to 7.2 in about eleven hours.

Tooling that iterates this quickly makes signatures based on User-Agent strings go stale within a day.

The Needles

The most reliable way to separate real exploit traffic from everything else so far has been the multipart boundary string. Each exploit kit we found uses a fixed or patterned boundary in its Content-Type header. We identified more than ten candidate kits this way, five of them tied to PHP2shell directly by name, User-Agent, or behavior.

The most widespread kit looks like this:

  • POST /index.php (or /), User-Agent php2shell-109618

  • Content-Type: multipart/form-data; boundary=B7kQpoc618
  • a fixed-size multipart body and a minimal header set

It also addresses its targets by raw IP address rather than hostname, and sends one or two requests per host. That's mass scanning across IP space rather than a campaign against particular sites. 

Body sizes vary widely from kit to kit, so don't rely on size thresholds alone to find this traffic.

Two Look-alikes to Rule out First

If you're hunting for PHP2shell in your own logs, two other campaigns will get in the way.

React2Shell (CVE-2025-55182). Two signatures circulating for PHP2shell are chunked multipart requests with no Content-Length, and bodies padded with long runs of the letter A. In the hour we measured, React2Shell accounted for 98.6% and 99.9% of those matches respectively. The padding signature was already almost entirely React2Shell the day before the release. React2Shell sprays every path, including /xmlrpc.php and /wp-login.php. The easy separator is the Next-Action request header, which Next.js Server Actions use and PHP never does.

Percentage of each circulating "PHP2shell" signature that is actually React2Shell, measured by the Next-Action header on POSTs to PHP endpoints, Oct 21 21:00–22:00 UTC. Same hour the day before the release: the padding signature was already 99.7% React2Shell.

wp2shell. This is a different bug entirely: a WordPress core unauthenticated RCE disclosed in July 2026. Its kit sends large multipart bodies to WordPress index.php, under a boundary starting ----wp2shell. It was active well before PHP2shell went public, so counting it in would invent a "pre-disclosure" php2shell story that isn't there.

A general lesson from both: a request-shape signature describes a type of request rather than an attacker. Compare against a baseline from before the disclosure, and split any increase by an identifying header or boundary before you attribute it.

What you Can and Can't See from the Edge

Exploit attempts are visible at the edge, but success is not. PHP2shell works by crashing or corrupting a PHP worker, so a successful attempt and a failed one can look the same from outside. If you think you were targeted, the confirmation lives on the origin:

  • PHP-FPM error logs

  • Apache error logs showing worker crashes (for example, AH00051 "child pid … exit signal" entries)

  • Unexpected outbound connections from PHP workers, since a connect-back shell is origin egress

What PHP2Shell says about AI

AI certainly helps vulnerability discovery, and does so much more quickly than in the past. Within about a day of the first tool appearing, we saw dozens of named tools from roughly 3,200 IPs, and one tool went from version 4.0 to 7.2 in about eleven hours. 

We can’t be certain whether any of them were written with AI. What we do observe is that the traffic itself is ordinary: Kits send a fixed multipart body to raw IP addresses, one or two requests per host, from the same kinds of VPN and hosting infrastructure behind most scanning. Regardless of the degree of AI assistance, we see a threat landscape that is growing larger and moving faster.

And so far, the defense story is still primarily a human story. Defenders checked their own systems and labeled the traffic. Hunters shared signatures that turned out to be almost entirely a different campaign. Patching, baselining, and sifting through origin logs is still largely people-driven work, even if AI-assisted, that requires humans to strategize about the right layered defenses in the right places.

What to do Now

  1. Patch. Upgrade to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or your distribution's patched package. If you're on 8.1 or older, talk to your package distributor about backports, or upgrade.

  2. Don't hunt red herrings. User-Agent, IP address, body sizes can all lead you astray when threat hunting. The real clue lies in the multipart boundary string.

  3. Rule out React2Shell and wp2shell before counting anything as PHP2shell.

  4. Be careful with IP blocklists. Several IPs on lists circulating for this bug are VPN exits, Tor exits, or residential and mobile addresses carrying ordinary traffic. Blocking them hurts real users.

  5. Fastly Next-Gen WAF customers: for applications protected by Next-Gen WAF, opt in to the signal covering both CVE-2026-109618 and CVE-2026-109619. It's a CVE-specific Templated Rule that works as a virtual patch, looking for structural signs of exploitation.
    See Fastly's post on proactive protection for more details.

Note: We also recommend considering our Automatic CVE Virtual Patching toggle within Workspace Configuration, shown below:

Indicators

How we Measured This

These figures come from Fastly Next-Gen WAF request telemetry between October 20 and October 22, 2026. Disclosure timing comes from the PHP Foundation's postmortem and GitHub's advisory records. Counts are lower bounds: we can only report requests our WAF saw and recorded, and some header-level figures come from matched sample hours rather than full days.

Read Fastly’s Proactive Protection for PHP2Shell, Critical PHP RCE CVE-2026-109618 and CVE-2026-109619 for more up-to-date information.

Prêt à commencer ?

Contactez-nous dès aujourd’hui