groundy
Infrastructure & Runtime

php2shell: Patch PHP Now or Trust WAF Virtual Patching?

php2shell parser bugs require runtime patching; WAF virtual patches offer only temporary containment. Cloudflare and ELLIO confirm that edge rules cannot fix the underlying R.

Published 10 references
A cracked green ceramic vessel with a copper-colored repair staple stands behind a curved black ceramic shield. Two ivory projectiles meet the shield while a third approaches, casting hard shadows on a warm ivory surface.
On this page13 sections

Patch the PHP runtime first; the WAF rules buy you a window, not a fix. That is the short answer to the php2shell question. The specifics below come mainly through two sources: Cloudflare’s disclosure post describes its own WAF deployment, and ELLIO Threat Research Lab supplies telemetry from its own deception network. The decision framework still holds even if individual numbers drift: the bugs live in the PHP parser, so no edge rule can repair them, and both the WAF vendor and the telemetry vendor agree patching is mandatory.

What php2shell actually is

php2shell is a pair of vulnerabilities in PHP core, disclosed on 2026-10-21 by Calif.io, Wiz, OpenAI and Quarkslab, and tracked as CVE-2026-109618 and CVE-2026-109619, according to Cloudflare’s write-up. Cloudflare describes the impact plainly: “The vulnerabilities affect all web servers running PHP 5.4 or later and they may allow an attacker to cause denial of service, remote code execution, and data exposure when an application processes specially crafted HTTP traffic.”

Two properties of that sentence drive every downstream decision. First, the flaw sits in the runtime, in code that executes while PHP parses a request, before any application logic runs. Updating WordPress, a Laravel dependency, or a plugin does nothing; the vulnerable code is the interpreter underneath them. Second, the trigger is crafted HTTP traffic, which is precisely the traffic a WAF inspects. That is why the patch-versus-virtual-patch question is even worth asking here, unlike for flaws that require authenticated access or local execution.

ELLIO, summarizing PHP’s advisories, reports both CVEs as CVSS 9.2 critical unauthenticated remote code execution. Treat the score as reported rather than confirmed; the qualitative point, that these are pre-authentication parser bugs, is consistent across both vendors.

Scope check: which PHP builds are exposed, and which get a patch

The fixed releases, published 2026-10-21, are PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12. Per ELLIO’s summary of the advisory, “PHP states that an update is the only complete fix for either bug. Patches exist only for the branches still supported, which means 8.2 and later.”

The harder news is for older deployments: “If you are running 8.1 or earlier, including every version of PHP 7 and PHP 5, there is no official fix for these two bugs and there will not be one. On those versions the remediation is a move to a supported branch.” Given that the affected range starts at PHP 5.4, every end-of-life branch in that range is permanently exposed. Cloudflare notes PHP powers almost 70% of websites; that is a large installed base, and part of it runs branches that will never be patched.

PHP branchPatch statusRequired action
8.5.xFixed in 8.5.12Upgrade, restart workers
8.4.xFixed in 8.4.27Upgrade, restart workers
8.3.xFixed in 8.3.36Upgrade, restart workers
8.2.xFixed in 8.2.35Upgrade, restart workers
5.4 through 8.1No upstream fix, none comingMigrate to a supported branch or isolate from the public internet

A note on where these numbers come from: the fixed-version matrix, CVSS ratings and interim mitigations arrive secondhand through the Cloudflare and ELLIO write-ups. PHP’s own advisories are public primary sources, ELLIO names the GitHub advisories (GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp), and the nwfella runbook points to NVD, CERT-EU and the Debian, Ubuntu and Red Hat trackers alongside them, so the numbers are worth checking at the source.

Triage order, in the order that actually protects you

The community runbooks fill in the operational steps that vendor announcements skip, and they matter more than the upgrade command itself.

1. Inventory every PHP binary that serves HTTP. The nwfella/php2shell-advisory runbook is explicit that php -v on a shell is not an inventory: web servers may load PHP through mod_php, PHP-FPM pools, containers with their own runtimes, or control panels that bundle a separate interpreter. Each of those is an independent exposure.

2. Upgrade, or verify a backport. Distros frequently patch without bumping the upstream version string, so a version check can report a vulnerable-looking number on a host that is actually fixed. The php2shell_scanner repository spells out the verification step: “A vendor backport can patch a flaw without bumping the version string. The tool prints the command that settles it on the host: rpm -q --changelog php | grep -i 109619.” The Debian-family equivalent is checking the package changelog for the CVE identifiers.

3. Restart every worker. This is the step most likely to be skipped, and skipping it leaves you exposed: “A patched package with stale workers is still vulnerable,” per the nwfella runbook. Long-lived PHP-FPM workers and mod_php processes keep the old code in memory until they are recycled.

4. If you cannot patch now, take the host off the public internet. That is the runbook’s blunt fallback, and given the exploitation timeline below, it is the honest one.

Interim mitigations are containment, not a fix

For hosts that need hours or days to schedule an upgrade, ELLIO reports per-CVE settings from PHP’s advisory. For CVE-2026-109618: “PHP’s advisory puts memory_limit=-1 first, enforced so nothing can restore a finite limit, and every worker restarted afterwards. Do not substitute RLIMIT_AS or RLIMIT_DATA; use container or cgroup memory limits instead.” For CVE-2026-109619: disable session.upload_progress.enabled, plus fatal_error_backtraces=0 on PHP 8.5. The nwfella runbook recommends setting session.upload_progress.enabled=0 even after patching.

Neither source pretends these are fixes. They remove the specific code paths the current exploits reach, which is exactly the kind of measure that holds until someone finds a neighboring path. Use them to survive a maintenance window, not to avoid one.

Why request-only WAF signatures were not enough

Cloudflare deployed managed rules about nine hours ahead of public disclosure, and from 03:02 UTC on 2026-10-21 all free and paid WAF customers were protected by default, per its post. Free plans received seven “Anomalous Execution” rules in the Cloudflare Managed Free Ruleset (77454fe2d30c4220b5701f6fdfb893ba); paid plans received seven more under the standard Managed Ruleset (efb7b8c949ac4650a09736fc376e9aee), plus a response-side rule (afffe22785674c80a31875c848d2242a) on all plans.

The interesting admission is what it took to get detection right: “Request data alone does not always provide enough evidence to distinguish an exploit from legitimate traffic. The origin’s response adds context, helping identify whether an exploit succeeded while reducing the risk of false positives.” Cloudflare built a new capability it calls full transaction detection, and the php2shell rules are the first managed detections to use it. Read that carefully if you run a different WAF: this is the vendor with arguably the most traffic visibility on the internet saying that matching exploit-shaped requests was insufficient. A request-signature rule on another platform may catch the noisy, known payloads and miss the ones that matter. None of these sources reports equivalent coverage from any non-Cloudflare WAF, so verify rather than assume.

How long does virtual patching hold?

Three separate lines of evidence say the answer is “not long, unless someone keeps maintaining it.”

First, Cloudflare itself reports watching attackers adapt: “We observed how attackers were mutating the attack payload to try to bypass our protections by tracking the volume of matches and cross-referencing with the rule logic.” Its analysts continue updating rules as variations emerge. That is an ongoing service commitment, not a one-time rule.

Second, academic work quantifies how fast static rules decay under mutation. In the WAFBooster evaluation across eight real-world WAFs, “WAFBooster improved the true rejection rate of mutated malicious payloads from 21% to 96%, with no false rejections.” The baseline matters more than the fix: existing rule sets caught only about a fifth of mutated payloads until new signatures were generated. Similarly, in the GenXSS experiments, 80% of LLM-generated XSS payloads bypassed a ModSecurity WAF running an industry-standard OWASP rule set; generating 15 new rules recovered coverage to 86% of previously successful attacks. The honest caveat: these are XSS and SQLi research payloads, not php2shell traffic, so the percentages do not transfer directly. What transfers is the mechanism, because Cloudflare observed the same payload mutation against its php2shell rules in the wild.

Third, there is a precedent from the same telemetry vendor. ELLIO’s wp2shell analysis documented mass exploitation within 48 hours of a July 2026 fix, and its lesson for rule authors was to “anchor on the structure and treat the string list as a supporting layer,” because toolkits were not optimizing for WAF evasion at first, but the window where simple filters work is measured in days.

The counterweight, also from ELLIO’s wp2shell post, is that in that campaign the toolkits were not optimizing for WAF evasion: keyword filters tuned to that week’s strings “would have caught nearly everything.” Edge rules genuinely absorb the first wave. The risk is what follows, once the rule set has drifted from the exploit paths; no source here dates that decay, and the patch is the only control that does not depend on staying current.

Exploitation predates the patch, so assume exposure

ELLIO’s self-reported sensor timeline is the strongest argument for post-exposure hunting. The first php2shell-shaped request hit its sensors at 08:36 UTC on 2026-10-21, five and a half hours before the fix was public; exploit-shaped requests for CVE-2026-109618 arrived at 16:05 UTC, and a multi-vulnerability scanning bot had folded php2shell into its run by 20:40 UTC the same night. This is one vendor’s deception network, not an internet-wide measurement, and independent verification of these exploitation claims is absent, but the direction is clear: probing started before most operators could have patched even if they had been watching for it.

That inverts the usual patch-then-relax sequence. If your host was reachable and unpatched during that window, patching stops future exploitation but says nothing about what already landed.

If you were exposed: hunting for what got planted

The point of a parser-level RCE, from an attacker’s perspective, is usually persistence. A Cisco Talos report cited in webshell-detection research found web shells in 35% of incidents in Q4 2024, up from 10% the prior quarter, and PHP remains the ecosystem where they are most entrenched. ELLIO’s php2shell hunt signals are concrete enough to act on:

  • PHP workers exiting on memory errors, in bursts rather than singletons
  • A PHP worker spawning a shell, interpreter, or network tool as a child process
  • User agents containing “php2shell” (ELLIO observed strings like php2shell-scan/109618)
  • Unexpected file writes in web-accessible directories; ELLIO identified one delivered payload as a 64-bit-server Mirai variant

On detection method, the research literature is sobering. Webshells “are continually adapted—often via obfuscation or encryption—to evade conventional detection pipelines,” per the Talos-citing paper, and ShellForge’s authors note that “existing detection mechanisms often struggle to keep pace with rapid variant evolution and sophisticated obfuscation techniques.” One practical consequence: opcode-level analysis largely sidesteps source-level obfuscation, because the paper observes that anti-detection techniques are “mostly used at the source code level,” though even an opcode pipeline still has to decode URL- and Base64-encoded operands. For teams evaluating scanners, the same sliding-window detection work publishes a dataset of 5,001 webshell and 5,936 benign PHP files. A single grep-based scan after patching is a starting point, not clearance. For anything high-value, the stronger move after a known-exposed window is rebuilding the host from a known-good image and rotating credentials, because absence of evidence in a noisy scan is weak evidence of absence.

The verdict

The decision resolves cleanly once the mechanism is clear. A runtime parser bug cannot be repaired by filtering traffic at the edge; the edge can only recognize shapes, and shapes mutate. Cloudflare, which profits when you trust its WAF, still writes that “customers should patch their PHP repos even with WAF protections providing an additional layer of defense-in-depth mitigation.” When the WAF vendor and the telemetry vendor and the runbook authors all say the same thing, the triage order is:

  1. Inventory every HTTP-serving PHP binary.
  2. Upgrade to 8.2.35, 8.3.36, 8.4.27 or 8.5.12, or verify a distro backport via the package changelog rather than the version string.
  3. Restart every worker, then verify.
  4. If you were exposed before patching, hunt for webshells and assume the scan undercounts.
  5. Keep the WAF rules enabled as a bridge and a tripwire; in Cloudflare, check Security > Security Analytics > Events and filter by the rule IDs above to see what is still arriving.
  6. For PHP 8.1 and older, no fix is coming. Migrate or isolate, and treat “the WAF covers it” as a temporary posture with an expiry date you do not control.

The limits deserve one last pass. The CVSS 9.2 ratings, the affected-version boundaries, and the interim-mitigation settings are secondhand reports of PHP’s advisories; the bypass percentages come from XSS and SQLi research, not php2shell payloads; and the exploitation timeline is one vendor’s sensor network. None of that weakens the ordering above, because the ordering rests on the one fact nobody disputes: the vulnerable code runs on your host, and only an updated, restarted runtime removes it. WAF rules change who sees the attack. The patch changes whether it works.

Frequently Asked Questions

Which PHP versions have a patch for php2shell?

The fixed releases, published 2026-10-21, are PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12. Per ELLIO’s summary of the advisory, “PHP states that an update is the only complete fix for either bug. Patches exist only for the branches still supported, which means 8.2 and later.”

What should I do if I am running PHP 8.1 or older?

The harder news is for older deployments: “If you are running 8.1 or earlier, including every version of PHP 7 and PHP 5, there is no official fix for these two bugs and there will not be one. On those versions the remediation is a move to a supported branch.”

Why is restarting workers necessary after patching?

This is the step most likely to be skipped, and skipping it leaves you exposed: “A patched package with stale workers is still vulnerable,” per the nwfella runbook. Long-lived PHP-FPM workers and mod_php processes keep the old code in memory until they are recycled.

References

Follow the links in the article for context. The supporting material is collected here for further reading.

  1. Cloudflare's disclosure postblog.cloudflare.comAccessed
  2. ELLIO Threat Research Labellio.techAccessed
  3. nwfella runbookgithub.comAccessed
  4. php2shell_scanner repositorygithub.comAccessed
  5. WAFBooster evaluationarxiv.orgAccessed
  6. GenXSS experimentsarxiv.orgAccessed
  7. wp2shell analysisellio.techAccessed
  8. ShellForge's authorsarxiv.orgAccessed
  9. sliding-window detection workarxiv.orgAccessed

Join the discussion

Share a useful perspective or ask a question about this article.

Discussion guidelinesComments privacy