Back
Research
Security

PHP2Shell: The PHP Core Flaws Site Owners Can’t Afford to Ignore

Huskeys Team
October 21, 2026
7
min read

PHP runs 69.3% of all websites with a known server-side language (W3Techs). php2shell puts every one of them at risk until patched.

Huskeys Research is tracking php2shell, the name given to CVE-2026-109618 and CVE-2026-109619: two critical, unauthenticated remote code execution vulnerabilities in PHP core. The flaws live in PHP's request parsing, which runs before any application, framework or CMS code. An attacker who exploits them can run code on the web server and reach anything that server can.

PHP is used by 69.3% of all websites whose server-side programming language is known (W3Techs, October 21, 2026). PHP's security team has rated both vulnerabilities 9.2 on the CVSS v4.0 scale (GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp), and every PHP version released in the last decade is affected by at least one of them, going back to 5.4 (Wiz).

This post covers what PHP has confirmed, how to patch, and the cross-vendor virtual patch we developed ahead of the official fix. End-of-life fleets carry the most risk: PHP 7 and PHP 5 together still account for about 35% of PHP sites (27.0% and 7.7%, W3Techs). PHP published official source backports for 7.4, 8.0 and 8.1, but those fleets must build or obtain patched binaries themselves, and PHP 7.3 and older receive no fix at all.

The fix is out now. Apply it immediately. PHP released 8.5.12, 8.4.27, 8.3.36 and 8.2.35 on October 21, 2026 at 13:30 UTC, a day earlier than originally planned (PHP release announcement, CERT-EU, PHP Foundation). Download from php.net/downloads or your distributor. On any server that can't be upgraded right away, deploy the interim virtual patch below on every edge in front of PHP.

Huskeys customers are already protected. Our research team delivered the virtual patch to customers at 07:25 UTC, about six hours before PHP's release. It was translated for every connected WAF and CDN and deployed across their full edge in a single step. As PHP and the edge vendors publish new markers, Huskeys updates the rule everywhere at once.

This post covers the vulnerability, mitigation and detection guidance, and a rule pack: activation and verification steps for the vendor protection Cloudflare, Akamai, Fastly and Imperva have shipped, and custom rules for AWS WAF, F5, Azure, Google Cloud Armor and ModSecurity.

The attack surface: session upload progress

Session upload progress is a built-in PHP feature that lets an application show a progress bar while a file uploads.

It works through a form field with a special name, set by session.upload_progress.name and PHP_SESSION_UPLOAD_PROGRESS by default. When a multipart POST includes that field, PHP starts writing progress data into the user's session while it is still receiving the request (PHP manual). The feature is enabled by default, and no application call to session_start() is required (PHP advisory).

From an attacker's perspective, that makes it an appealing target: code in PHP core that any client can trigger with a single unauthenticated request, on almost any PHP server.

The vulnerability

PHP has published the root cause in two advisories. CVE-2026-109618 (GHSA-w8hw-w9fx-j79w) is remote code execution via out-of-memory corruption. CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp) is remote code execution via stale global state, and PHP lists the session upload progress lifecycle as a confirmed path. The confirmed impact is unauthenticated heap use-after-free, memory disclosure and remote code execution. On the upload-progress path, the attack looks like this:

  1. The attacker sends an unauthenticated multipart POST to any PHP endpoint, including the PHP_SESSION_UPLOAD_PROGRESS field.
  2. PHP's request parser starts tracking upload progress and writes to the session, before any application code runs.
  3. Memory exhaustion during input processing, the application-independent trigger PHP demonstrated, leaves stale state behind and leads to a heap use-after-free.
  4. The memory corruption is turned into code execution in the PHP worker, giving the attacker a shell on the server.

Because step 2 happens inside PHP itself, no application fix, plugin update or framework setting helps. Only the PHP build, or a control in front of it, changes the outcome. PHP considers the risk highest for mod_php, and every SAPI that serves requests is at risk (PHP).

Claim

Status (October 21, 2026)

Two critical RCEs in PHP core, CVSS v4.0 9.2 each

Confirmed by PHP

Security release

Shipped October 21, 2026 at 13:30 UTC, moved up from October 22 (CERT-EU)

Fixed builds 8.5.12, 8.4.27, 8.3.36, 8.2.35

Official (PHP)

PHP 7.4, 8.0 and 8.1

Official minimal source backports from PHP. PHP 7.3 and older get no fix.

CVE records

Published, with root cause in both PHP advisories

Heap use-after-free in session upload progress

Confirmed by PHP (CVE-2026-109619, advisory)

Remote code execution

Confirmed by PHP against mod_php with default settings (advisory); demonstrated by Wiz and CrowdStrike

Discovery

Credited to Calif, OpenAI and Wiz (PHP Foundation). watchTowr reproduced CVE-2026-109618 before the release (SecurityWeek).

Exploited in the wild

Exploitation attempts observed by multiple vendors (SecurityWeek), but no publicly confirmed successful compromise (watchTowr). Neither CVE is in the CISA KEV catalog (CISA).

About the public PoCs. Public exploit code is circulating, and exploitation attempts are being observed. Do not run it.

Mitigation & detection

First, find every PHP runtime you expose. Check the version on each origin, and from outside, see whether a host advertises PHP:

BASH
b64:cGhwIC12CnBocC1mcG0gLWkgfCBncmVwIC1pIHVwbG9hZF9wcm9ncmVzcwpjdXJsIC1zSSBodHRwczovL3d3dy5leGFtcGxlLmNvbS8gfCBncmVwIC1pRSAiXihzZXJ2ZXJ8eC1wb3dlcmVkLWJ5KToi

Check the upload-progress setting from the web SAPI (FPM, mod_php, LiteSpeed), not only the CLI, since they often load different configs. Then map every path to those origins: CDN, load balancer, alternate hostnames and directly reachable origin IPs.

The fix shipped in PHP's October 21 security release: 8.5.12, 8.4.27, 8.3.36 and 8.2.35, plus official minimal source backports for 7.4, 8.0 and 8.1 (release announcement). PHP 7.3 and older get no fix. We strongly recommend that you:

  • Upgrade PHP now from php.net/downloads or a trusted distributor, then restart PHP-FPM, Apache (mod_php) or your other PHP workers.
  • Ensure no PHP origin is reachable around your edge. A WAF rule protects only the traffic that passes through it.

If you can't upgrade immediately, or you run 8.1 or older, apply all three interim layers:

  1. Edge: block POST bodies containing the PHP_SESSION_UPLOAD_PROGRESS form field (rule pack below). This covers the upload-progress path, one confirmed path of CVE-2026-109619, and normal uploads without the field keep working. Other exploit shapes exist, so treat it as partial coverage.
  2. Origin: apply PHP's interim settings in the web SAPI config and reload workers (PHP):
    • Set session.upload_progress.enabled = Off. PHP notes this blocks the confirmed session paths but not other variants, so it is not sufficient alone.
    • Set memory_limit = -1, preferably in a way applications can't override.
    • Keep post_max_size low (for example 8M, or 32M where uploads need it).
    • On PHP 8.5, set fatal_error_backtraces = 0.
    • Remove per-request INI diagnostics.
  3. Patch as soon as you can. Patching is the only complete fix; the edge rule buys time and does not repair the code.

While the interim rule is active, hunt for:

  • Rule hits, and multipart POSTs to routes that never accept uploads
  • PHP fatals, segfaults or worker restarts around those requests
  • PHP or PHP-FPM spawning shells, curl or whoami (SOC Prime, CISA)
  • New admin users, web shells, or unexpected PHP files in document roots and upload folders

Huskeys customers already have the interim virtual patch available across every connected edge vendor. See How can Huskeys help? below.

‍

The cross-vendor rule pack

Deploy in Block now. php2shell is a Critical, unauthenticated RCE under active exploitation attempts, so every rule below goes straight to Block or Deny with fast owner sign-off, not Count or Log first. A brief Count or preview pass is only a sidebar for a specific false-positive concern on a high-traffic app, and even then keep it short.

A WAF does not repair the vulnerable code path. It buys a containment window while you patch. Labels: INTERIM virtual patch. Patch PHP first, then remove the broad custom rules (see "Patch, verify, then remove the broad rule" below).

Cloudflare, Akamai, Fastly and Imperva already ship protection for these CVEs: confirm it is enabled and in Block. AWS WAF, F5, Azure, Google Cloud Armor and ModSecurity need the custom rules in this pack.

  • Cloudflare: managed rules shipped (seven "Generic Rules - Anomalous Execution" per ruleset, plus a Response Firewall rule), live and on Block by default since 03:02 UTC on October 21. Action: confirm Block, remove downgrading overrides.
  • Akamai App & API Protector: Adaptive Security Engine rule 3000991 v1 shipped. Action: confirm enabled and set to Deny, then activate.
  • Fastly Next-Gen WAF: CVE-specific Templated Rule (Virtual Patch) shipped, auto-updating. Action: enable it with immediate blocking.
  • Imperva: built-in protection shipped (Cloud WAF out of the box; Elastic WAF and WAF Gateway after updates), no public rule ID. Action: apply the updates, confirm the action is Block, and optionally add a custom rule, Alert first.
  • AWS WAF: no php2shell managed rule. Action: three custom Web ACL rules, Block, on every CloudFront distribution and ALB that fronts PHP.
  • F5 Distributed Cloud / BIG-IP Advanced WAF: no php2shell signature found. Action: Deny service-policy rules / custom attack signature in blocking.
  • Azure Front Door / Application Gateway: no php2shell managed rule. Action: custom Match Rule, Block, and keep DRS/CRS managed rule sets on.
  • Google Cloud Armor: no php2shell managed rule. Action: CEL rules as active deny-403 (no --preview).
  • ModSecurity / OWASP CRS: local rules, block, loaded before the CRS blocking evaluation.

Every custom rule encodes the same predicate: block a POST that reaches PHP and shows a php2shell hallmark, namely a PHP_SESSION_UPLOAD_PROGRESS field (CVE-2026-109619), an oversized application/x-www-form-urlencoded body, a urlencoded body beginning with = followed by repeated [ brackets, or a multipart part with filename="" plus deeply bracket-nested field names (CVE-2026-109618). Do not rely on one edge alone: a CDN rule does not protect a load balancer, origin IP or alternate hostname that is reachable directly.

‍

Cloudflare: confirm the shipped managed rules are in Block

Cloudflare customers on free or paid WAF have been protected by default since 03:02 UTC on October 21, 2026. The titles are generic ("Generic Rules - Anomalous Execution"), do not name the CVEs, and Cloudflare says descriptions will be updated, so filter and verify by rule ID, not by title. All fourteen request rules (seven per ruleset) ship with action Block.

Activate / verify:

  1. In the dashboard go to Security > WAF > Managed rules.
  2. Confirm the Cloudflare Managed Ruleset (Free plan: the Cloudflare Managed Free Ruleset) and the Cloudflare Managed Response Firewall Default (all plans, including Free) are deployed and the action is Block, not Log.
  3. Remove any ruleset-level override that downgrades these rules to Log.
  4. In Security > Security Analytics > Events, filter by the rule IDs below and confirm matches are being blocked.
  5. Have the WAF owner confirm Block on every zone that fronts PHP, then leave them on. This is a confirm-and-sign-off step, not a Count soak.

Paid plans (Pro, Business, Enterprise): Cloudflare Managed Ruleset, ruleset ID efb7b8c949ac4650a09736fc376e9aee, seven rules:

  • c1d409ad838f47f59446f21cc438d5dd
  • f9a45927414245e3a64bb12d0a0c2f64
  • 771c654ae1154d4daa72f4e1545d163c
  • 217005865f0145f392300bac212afcc9
  • 38efdb17ea594501bb55a1d71867b12e
  • 4148ae686fdb4d01be673fab9d9c073d
  • 1389aa259617437392ce149c48143645

Free plan: Cloudflare Managed Free Ruleset, ruleset ID 77454fe2d30c4220b5701f6fdfb893ba, seven rules (Free IDs differ from paid IDs):

  • 4e315666e8e344cdb64119a3b5ccf32f
  • 00c10e6cc4da4bd08722adeebcd95984
  • 48ddf592361e4fdb97a8a85982dab691
  • 3556683d512a47f2937899c524b1a4c6
  • da07c85852e34625bb478496302ede46
  • 19373a83abb840e18ab3ce5a7975ce1b
  • 07f0b6ddaabf40b1b2042dade8d613fa

Response Firewall (all plans, including Free): Cloudflare Managed Response Firewall Default, rule ID afffe22785674c80a31875c848d2242a ("Default block for High confidence Signatures"). It blocks a matching origin response before it reaches the requester, which matters because attackers are mutating request-only payloads. Confirm it is on and blocking.

‍

Fastly Next-Gen WAF: enable the CVE templated rule in Block

Fastly shipped a CVE-specific Templated Rule (Virtual Patch) for CVE-2026-109618 and CVE-2026-109619 and recommends applying it to all Edge and On-prem services that may be vulnerable. Opted-in customers receive updates automatically. Fastly does not publish the rule logic.

Next-Gen WAF control panel (standalone console, formerly Signal Sciences; Professional/Premier or Security Core/Core Plus/Total):

  1. Log in to the Next-Gen WAF control panel.
  2. From the Sites menu, select the site.
  3. From the Rules menu, select Templated Rules.
  4. Click View next to the Virtual Patch for CVE-2026-109618 & CVE-2026-109619.
  5. Click Configure, then Add trigger.
  6. Select the "Block requests from an IP immediately if the CVE-... signal is observed" checkbox. Fastly documents the generic CVE-YYYY-NNNNN label; the exact signal name for this two-CVE patch is not published, so pick the php2shell virtual patch signal.
  7. Click Update rule.

Fastly control panel (manage.fastly.com, workspace-based Next-Gen WAF), either path:

  • Signals page: Security > Next-Gen WAF > Signals, choose the workspace, open the Virtual Patches tab, click the php2shell patch, click Configuration then Detections and set Status On, then on the Thresholds tab set Immediate blocking On.
  • Workspace settings: Security > Next-Gen WAF > Workspaces, click the gear next to the workspace, click Virtual patches, search for the patch, set Mode to Block (only offered when the workspace is in blocking mode) and set Status On.

Which path applies depends on whether the account uses control panel workspaces or the standalone console (corps and sites). On the Essential platform, Fastly documents only adding a detection and an alert on the CVE signal; confirm with Fastly how to get immediate blocking on Essential. Do not leave it on log.

‍

Akamai App & API Protector: ASE rule 3000991 in Deny

App & API Protector ships Adaptive Security Engine rule 3000991 v1, "PHP Remote Code Execution Attack Detected (CVE-2026-109618, CVE-2026-109619)."

Activate in the console: open App & API Protector > your security configuration > Adaptive Security Engine, confirm rule 3000991 is enabled and set to Deny (automatic/deny mode), not alert-only, then activate the configuration to staging and then production. Put any related custom rules above broad exception rules.

Manual ASE control via API (Akamai "Modify the action for a rule"; EdgeGrid auth, base URL https://{hostname}/appsec/v1):

HTTP
b64:UFVUIC9hcHBzZWMvdjEvY29uZmlncy97Y29uZmlnSWR9L3ZlcnNpb25zL3t2ZXJzaW9uTnVtYmVyfS9zZWN1cml0eS1wb2xpY2llcy97cG9saWN5SWR9L3J1bGVzLzMwMDA5OTEKCnsiYWN0aW9uIjoiZGVueSJ9

The action enum is alert, deny or none. Akamai states this applies to both ASE auto and ASE manual upgrade modes, but rule actions for ASE_AUTO are currently in beta; contact your account team to access it. A 404 means the policy lacks application layer controls or rule 3000991 is not available in that policy. Edit a non-active configuration version, then activate it to staging and production.

‍

Imperva: apply the shipped protection, then confirm Block

Imperva says its WAF provides a virtual patch for php2shell: "Cloud WAF customers are protected against this vulnerability out of the box. Elastic WAF customers receive the same protection following a software update. WAF Gateway customers are protected following a software update and an ADC security content update" (Imperva). The protection inspects anomalous multipart payloads, repeated session-related fields, abnormal response headers and suspicious session upload-progress activity.

  • Imperva Cloud WAF: protected out of the box, per Imperva. Nothing to enable.
  • Imperva Elastic WAF: upgrade to Elastic WAF 2.2.1 (Elastic WAF 2.2.1 release notes).
  • Imperva WAF Gateway (SecureSphere): apply the ADC Security Content Update dated October 20, 2026 and apply the newly added policies to all applicable services; install WAF v14.7 Patch 221 or v15.5 Patch 21; then manually configure the additional security policy described in Imperva's linked KB article (v14.7 release notes, v15.5 release notes).

Verify the action. Imperva does not publish a rule ID for this protection or state whether it blocks or alerts by default. Check Security Events and your site's WAF settings in the Imperva console, and confirm the action is Block.

Optional custom rule for Imperva Cloud WAF. Imperva's built-in protection is already in place, so this rule is defense in depth. Add it under Application > Websites > your site > Security > Rules > Add Rule. Start with action Alert (optionally with Test Rule Only on My IP Addresses), review Security Events for hits and false positives, then switch to Block Request after a human signs off.

Rule I1: upload-progress field on a multipart POST (php2shell)

INCAPRULES
b64:TWV0aG9kID09IFBPU1QgJiBDb250ZW50LVR5cGUgY29udGFpbnMgIm11bHRpcGFydC9mb3JtLWRhdGEiICYgQWxsUGFyYW1zUHJlc2VudCA9PSB0cnVlICYgKEJvZHlQYXJhbUV4aXN0cyA9PSAiUEhQX1NFU1NJT05fVVBMT0FEX1BST0dSRVNTIiB8IFBvc3REYXRhIGNvbnRhaW5zICJQSFBfU0VTU0lPTl9VUExPQURfUFJPR1JFU1MiKQ==

Imperva does not document whether parameter parsing covers multipart part names, so the rule pairs BodyParamExists with a raw PostData match (Imperva rule filter parameters). If you changed session.upload_progress.name, replace PHP_SESSION_UPLOAD_PROGRESS with your value. Apps that genuinely use session upload progress will match; exclude a known-good path with & URL != "/path" if needed.

Rule I2 (optional, Alert only): oversized multipart POST

INCAPRULES
b64:TWV0aG9kID09IFBPU1QgJiBDb250ZW50LVR5cGUgY29udGFpbnMgIm11bHRpcGFydC9mb3JtLWRhdGEiICYgUG9zdFNpemUgPiBUSFJFU0hPTERfQllURVM=

Set THRESHOLD_BYTES per site, at or just above the app's real maximum upload (often its post_max_size). Keep this rule in Alert: legitimate uploads are large by design, so a fixed threshold can block real users. Remove both custom rules once every PHP runtime behind the site is patched; Imperva's built-in protection stays on.

‍

AWS WAF (WAFv2)

There is no php2shell-specific AWS managed rule. AWSManagedRulesPHPRuleSet targets PHP code-injection patterns, not these CVEs. Deploy all three rules below with Action: Block now, on every CloudFront distribution and ALB that fronts PHP. The only reason to park one in Count briefly is owner sign-off on a high-traffic app with large legitimate forms. Priorities 200 to 202 are examples: confirm your Web ACL's existing priorities first, since a collision rejects the update.

Body inspection limits:

  • ALB and AppSync: fixed at 8 KB (8,192 bytes); cannot be raised.
  • CloudFront, API Gateway, Cognito, App Runner, Verified Access and Amazon Bedrock AgentCore Gateway: 16 KB default (16,384 bytes), raisable in 16 KB steps up to 64 KB via the Web ACL AssociationConfig.
  • WAFv2 cannot assert a true 1 to 2 MB body size, so the oversized rule keys off the inspection window with OversizeHandling: MATCH.

Rule A1: upload-progress field on a multipart POST (CVE-2026-109619)

JSON
b64:ewogICJOYW1lIjogInZpcnR1YWwtcGF0Y2gtY3ZlLTIwMjYtMTA5NjE5LXByb2dyZXNzLWZpZWxkIiwKICAiUHJpb3JpdHkiOiAyMDAsCiAgIkFjdGlvbiI6IHsgIkJsb2NrIjoge30gfSwKICAiVmlzaWJpbGl0eUNvbmZpZyI6IHsKICAgICJTYW1wbGVkUmVxdWVzdHNFbmFibGVkIjogdHJ1ZSwKICAgICJDbG91ZFdhdGNoTWV0cmljc0VuYWJsZWQiOiB0cnVlLAogICAgIk1ldHJpY05hbWUiOiAidmlydHVhbC1wYXRjaC1jdmUtMjAyNi0xMDk2MTktcHJvZ3Jlc3MtZmllbGQiCiAgfSwKICAiU3RhdGVtZW50IjogewogICAgIkFuZFN0YXRlbWVudCI6IHsKICAgICAgIlN0YXRlbWVudHMiOiBbCiAgICAgICAgewogICAgICAgICAgIkJ5dGVNYXRjaFN0YXRlbWVudCI6IHsKICAgICAgICAgICAgIlNlYXJjaFN0cmluZyI6ICJtdWx0aXBhcnQvZm9ybS1kYXRhIiwKICAgICAgICAgICAgIkZpZWxkVG9NYXRjaCI6IHsgIlNpbmdsZUhlYWRlciI6IHsgIk5hbWUiOiAiY29udGVudC10eXBlIiB9IH0sCiAgICAgICAgICAgICJUZXh0VHJhbnNmb3JtYXRpb25zIjogW3sgIlByaW9yaXR5IjogMCwgIlR5cGUiOiAiTE9XRVJDQVNFIiB9XSwKICAgICAgICAgICAgIlBvc2l0aW9uYWxDb25zdHJhaW50IjogIkNPTlRBSU5TIgogICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgIkJ5dGVNYXRjaFN0YXRlbWVudCI6IHsKICAgICAgICAgICAgIlNlYXJjaFN0cmluZyI6ICJQSFBfU0VTU0lPTl9VUExPQURfUFJPR1JFU1MiLAogICAgICAgICAgICAiRmllbGRUb01hdGNoIjogeyAiQm9keSI6IHsgIk92ZXJzaXplSGFuZGxpbmciOiAiQ09OVElOVUUiIH0gfSwKICAgICAgICAgICAgIlRleHRUcmFuc2Zvcm1hdGlvbnMiOiBbeyAiUHJpb3JpdHkiOiAwLCAiVHlwZSI6ICJVUkxfREVDT0RFIiB9XSwKICAgICAgICAgICAgIlBvc2l0aW9uYWxDb25zdHJhaW50IjogIkNPTlRBSU5TIgogICAgICAgICAgfQogICAgICAgIH0KICAgICAgXQogICAgfQogIH0KfQ==

AWS applies text transforms to the request component, not to SearchString, so keep exact-case PHP_SESSION_UPLOAD_PROGRESS with URL_DECODE only. Apps that genuinely use session upload progress will match; scope by host or URI if you know the feature is unused.

Rule A2: oversized urlencoded POST (CVE-2026-109618)

JSON
b64:ewogICJOYW1lIjogInZpcnR1YWwtcGF0Y2gtY3ZlLTIwMjYtMTA5NjE4LW92ZXJzaXplZC11cmxlbmNvZGVkIiwKICAiUHJpb3JpdHkiOiAyMDEsCiAgIkFjdGlvbiI6IHsgIkJsb2NrIjoge30gfSwKICAiVmlzaWJpbGl0eUNvbmZpZyI6IHsKICAgICJTYW1wbGVkUmVxdWVzdHNFbmFibGVkIjogdHJ1ZSwKICAgICJDbG91ZFdhdGNoTWV0cmljc0VuYWJsZWQiOiB0cnVlLAogICAgIk1ldHJpY05hbWUiOiAidmlydHVhbC1wYXRjaC1jdmUtMjAyNi0xMDk2MTgtb3ZlcnNpemVkLXVybGVuY29kZWQiCiAgfSwKICAiU3RhdGVtZW50IjogewogICAgIkFuZFN0YXRlbWVudCI6IHsKICAgICAgIlN0YXRlbWVudHMiOiBbCiAgICAgICAgewogICAgICAgICAgIkJ5dGVNYXRjaFN0YXRlbWVudCI6IHsKICAgICAgICAgICAgIlNlYXJjaFN0cmluZyI6ICJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQiLAogICAgICAgICAgICAiRmllbGRUb01hdGNoIjogeyAiU2luZ2xlSGVhZGVyIjogeyAiTmFtZSI6ICJjb250ZW50LXR5cGUiIH0gfSwKICAgICAgICAgICAgIlRleHRUcmFuc2Zvcm1hdGlvbnMiOiBbeyAiUHJpb3JpdHkiOiAwLCAiVHlwZSI6ICJMT1dFUkNBU0UiIH1dLAogICAgICAgICAgICAiUG9zaXRpb25hbENvbnN0cmFpbnQiOiAiQ09OVEFJTlMiCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiU2l6ZUNvbnN0cmFpbnRTdGF0ZW1lbnQiOiB7CiAgICAgICAgICAgICJGaWVsZFRvTWF0Y2giOiB7ICJCb2R5IjogeyAiT3ZlcnNpemVIYW5kbGluZyI6ICJNQVRDSCIgfSB9LAogICAgICAgICAgICAiQ29tcGFyaXNvbk9wZXJhdG9yIjogIkdUIiwKICAgICAgICAgICAgIlNpemUiOiA4MTkyLAogICAgICAgICAgICAiVGV4dFRyYW5zZm9ybWF0aW9ucyI6IFt7ICJQcmlvcml0eSI6IDAsICJUeXBlIjogIk5PTkUiIH1dCiAgICAgICAgICB9CiAgICAgICAgfQogICAgICBdCiAgICB9CiAgfQp9

Set Size to the body inspection limit of the associated resource: 8192 for ALB and AppSync; 16384 for CloudFront, API Gateway, Cognito, App Runner, Verified Access or Bedrock AgentCore Gateway at the default 16 KB; otherwise your configured limit (for example 65536 at 64 KB). The 8192 above is correct for ALB only; on a default CloudFront ACL it would wrongly match urlencoded bodies between 8 and 16 KB. On ALB this blocks every urlencoded POST over 8 KB, so check large-form traffic and exclude known large-POST paths (bulk import APIs, webhook relays). Do not put a SizeConstraintStatement on the Content-Length header: WAF measures the header string length, not the body size.

Rule A3: multipart hallmarks on PHP routes (CVE-2026-109618 / 109619)

JSON
b64:ewogICJOYW1lIjogInZpcnR1YWwtcGF0Y2gtY3ZlLTIwMjYtMTA5NjE4LW11bHRpcGFydC1oYWxsbWFyayIsCiAgIlByaW9yaXR5IjogMjAyLAogICJBY3Rpb24iOiB7ICJCbG9jayI6IHt9IH0sCiAgIlZpc2liaWxpdHlDb25maWciOiB7CiAgICAiU2FtcGxlZFJlcXVlc3RzRW5hYmxlZCI6IHRydWUsCiAgICAiQ2xvdWRXYXRjaE1ldHJpY3NFbmFibGVkIjogdHJ1ZSwKICAgICJNZXRyaWNOYW1lIjogInZpcnR1YWwtcGF0Y2gtY3ZlLTIwMjYtMTA5NjE4LW11bHRpcGFydC1oYWxsbWFyayIKICB9LAogICJTdGF0ZW1lbnQiOiB7CiAgICAiQW5kU3RhdGVtZW50IjogewogICAgICAiU3RhdGVtZW50cyI6IFsKICAgICAgICB7CiAgICAgICAgICAiQnl0ZU1hdGNoU3RhdGVtZW50IjogewogICAgICAgICAgICAiU2VhcmNoU3RyaW5nIjogIm11bHRpcGFydC9mb3JtLWRhdGEiLAogICAgICAgICAgICAiRmllbGRUb01hdGNoIjogeyAiU2luZ2xlSGVhZGVyIjogeyAiTmFtZSI6ICJjb250ZW50LXR5cGUiIH0gfSwKICAgICAgICAgICAgIlRleHRUcmFuc2Zvcm1hdGlvbnMiOiBbeyAiUHJpb3JpdHkiOiAwLCAiVHlwZSI6ICJMT1dFUkNBU0UiIH1dLAogICAgICAgICAgICAiUG9zaXRpb25hbENvbnN0cmFpbnQiOiAiQ09OVEFJTlMiCiAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICB7CiAgICAgICAgICAiT3JTdGF0ZW1lbnQiOiB7CiAgICAgICAgICAgICJTdGF0ZW1lbnRzIjogWwogICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJCeXRlTWF0Y2hTdGF0ZW1lbnQiOiB7CiAgICAgICAgICAgICAgICAgICJTZWFyY2hTdHJpbmciOiAiUEhQX1NFU1NJT05fVVBMT0FEX1BST0dSRVNTIiwKICAgICAgICAgICAgICAgICAgIkZpZWxkVG9NYXRjaCI6IHsgIkJvZHkiOiB7ICJPdmVyc2l6ZUhhbmRsaW5nIjogIkNPTlRJTlVFIiB9IH0sCiAgICAgICAgICAgICAgICAgICJUZXh0VHJhbnNmb3JtYXRpb25zIjogW3sgIlByaW9yaXR5IjogMCwgIlR5cGUiOiAiVVJMX0RFQ09ERSIgfV0sCiAgICAgICAgICAgICAgICAgICJQb3NpdGlvbmFsQ29uc3RyYWludCI6ICJDT05UQUlOUyIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICJCeXRlTWF0Y2hTdGF0ZW1lbnQiOiB7CiAgICAgICAgICAgICAgICAgICJTZWFyY2hTdHJpbmciOiAiZmlsZW5hbWU9XCJcIiIsCiAgICAgICAgICAgICAgICAgICJGaWVsZFRvTWF0Y2giOiB7ICJCb2R5IjogeyAiT3ZlcnNpemVIYW5kbGluZyI6ICJDT05USU5VRSIgfSB9LAogICAgICAgICAgICAgICAgICAiVGV4dFRyYW5zZm9ybWF0aW9ucyI6IFt7ICJQcmlvcml0eSI6IDAsICJUeXBlIjogIkxPV0VSQ0FTRSIgfV0sCiAgICAgICAgICAgICAgICAgICJQb3NpdGlvbmFsQ29uc3RyYWludCI6ICJDT05UQUlOUyIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICB9CiAgICAgICAgICAgIF0KICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIF0KICAgIH0KICB9Cn0=

There is no URI allowlist on this rule: any PHP path can carry the exploit. If you must exempt a genuine upload endpoint, use an exact-path NotStatement for that endpoint (for example /wp-admin/async-upload.php) and review it, rather than a broad prefix.

‍

F5 Distributed Cloud and BIG-IP Advanced WAF

No php2shell-specific F5 attack signature is listed for BIG-IP ASM/Advanced WAF or Distributed Cloud at the time of writing. Treat the field values below as a starting point a human validates in the console.

  • F5 Distributed Cloud (HTTP Load Balancer service policy): a Deny rule for method POST where the body or a multipart part name contains PHP_SESSION_UPLOAD_PROGRESS, and a second Deny rule for POST with Content-Type containing multipart/form-data carrying filename="" with bracket-nested field names, or an oversized application/x-www-form-urlencoded body. Place both before broad allow or routing rules, since rules evaluate in order. Validate on a non-production namespace or in report mode briefly, then enforce Deny on every load balancer and origin path that serves the site.
  • F5 BIG-IP Advanced WAF: a user-defined attack signature matching the multipart part-header line carrying a non-empty PHP_SESSION_UPLOAD_PROGRESS content-disposition name, with apply_to = Request, risk/accuracy High, bound to a manual signature set with block and alarm on, in a policy set to blocking (not transparent/staging) once validated. Validate the exact RE2 and signature-set binding against your BIG-IP version, then enforce blocking on every virtual server that fronts PHP.

‍

Azure WAF: Front Door and Application Gateway

Set the custom rule action to Block now (not Log) on every Front Door policy and App Gateway listener that fronts PHP, and keep the managed rule sets on (DRS on Front Door, CRS on Application Gateway).

Custom rule (Front Door / Azure WAF policy):

  • Rule name: virtual-patch-cve-2026-109619-progress-field
  • Priority: 200, Rule type: Match, Action: Block
  • Condition 1: Match variable RequestBody, Operator Contains, Value PHP_SESSION_UPLOAD_PROGRESS, Transform UrlDecode
  • Condition 2 (AND): Match variable RequestHeader, Selector Content-Type, Operator Contains, Value multipart/form-data, Transform Lowercase

A second rule, virtual-patch-cve-2026-109618-oversized-form, covers the oversized urlencoded shape: RequestHeader Content-Type Contains application/x-www-form-urlencoded, AND RequestBody GreaterThanOrEqual a byte threshold inside the inspectable window (about 100000 on Front Door). Confirm the size operator's behavior in your policy before relying on it.

Body inspection limits: Azure Front Door WAF inspects only the first 128 KB of the body. Application Gateway WAF v2 (CRS 3.2+) exposes request_body_inspect_limit_in_kb (default 128) separately from max_request_body_size_in_kb (default 128); with default enforcement the gateway rejects bodies over 128 KB, which can break legitimate large uploads, so set the limits deliberately with the app owner.

‍

Google Cloud Armor

Deploy each rule as an active deny-403 rule now (no --preview), at a priority ahead of broad allows, attached to every external ALB backend serving PHP. Inspecting request.body in custom rules uses advanced match conditions that are in Preview (since August 27, 2026); if request.body is rejected, your project lacks the capability, so fall back to the preconfigured WAF rules plus origin monitoring. Google types request.body as a raw string; if has(request.body) is rejected, drop it and rely on request.body.contains(...). Rules 201 and 202 use five subexpressions, the documented maximum, so do not add more terms.

BASH
b64:IyBSZXBsYWNlIFBPTElDWSB3aXRoIHlvdXIgc2VjdXJpdHkgcG9saWN5IG5hbWUuIEFjdGl2ZSBkZW55LCBubyAtLXByZXZpZXcuCgpnY2xvdWQgY29tcHV0ZSBzZWN1cml0eS1wb2xpY2llcyBydWxlcyBjcmVhdGUgMjAwIFwKICAtLXNlY3VyaXR5LXBvbGljeT1QT0xJQ1kgXAogIC0tZXhwcmVzc2lvbj0ncmVxdWVzdC5tZXRob2QgPT0gIlBPU1QiICYmIGhhcyhyZXF1ZXN0LmJvZHkpICYmIHJlcXVlc3QuYm9keS5jb250YWlucygiUEhQX1NFU1NJT05fVVBMT0FEX1BST0dSRVNTIiknIFwKICAtLWFjdGlvbj1kZW55LTQwMyBcCiAgLS1kZXNjcmlwdGlvbj0ndmlydHVhbC1wYXRjaC1jdmUtMjAyNi0xMDk2MTk6IHVwbG9hZC1wcm9ncmVzcyBmaWVsZCBpbiBib2R5JwoKZ2Nsb3VkIGNvbXB1dGUgc2VjdXJpdHktcG9saWNpZXMgcnVsZXMgY3JlYXRlIDIwMSBcCiAgLS1zZWN1cml0eS1wb2xpY3k9UE9MSUNZIFwKICAtLWV4cHJlc3Npb249J3JlcXVlc3QubWV0aG9kID09ICJQT1NUIiAmJiBoYXMocmVxdWVzdC5oZWFkZXJzWyJjb250ZW50LXR5cGUiXSkgJiYgcmVxdWVzdC5oZWFkZXJzWyJjb250ZW50LXR5cGUiXS5sb3dlcigpLmNvbnRhaW5zKCJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQiKSAmJiBoYXMocmVxdWVzdC5oZWFkZXJzWyJjb250ZW50LWxlbmd0aCJdKSAmJiByZXF1ZXN0LmhlYWRlcnNbImNvbnRlbnQtbGVuZ3RoIl0ubWF0Y2hlcygiXlswLTldezcsfSQiKScgXAogIC0tYWN0aW9uPWRlbnktNDAzIFwKICAtLWRlc2NyaXB0aW9uPSd2aXJ0dWFsLXBhdGNoLWN2ZS0yMDI2LTEwOTYxODogb3ZlcnNpemVkIHVybGVuY29kZWQgUE9TVCAoNysgZGlnaXQgQ29udGVudC1MZW5ndGgpJwoKZ2Nsb3VkIGNvbXB1dGUgc2VjdXJpdHktcG9saWNpZXMgcnVsZXMgY3JlYXRlIDIwMiBcCiAgLS1zZWN1cml0eS1wb2xpY3k9UE9MSUNZIFwKICAtLWV4cHJlc3Npb249J3JlcXVlc3QubWV0aG9kID09ICJQT1NUIiAmJiBoYXMocmVxdWVzdC5oZWFkZXJzWyJjb250ZW50LXR5cGUiXSkgJiYgcmVxdWVzdC5oZWFkZXJzWyJjb250ZW50LXR5cGUiXS5sb3dlcigpLmNvbnRhaW5zKCJtdWx0aXBhcnQvZm9ybS1kYXRhIikgJiYgaGFzKHJlcXVlc3QuYm9keSkgJiYgcmVxdWVzdC5ib2R5LmNvbnRhaW5zKCJmaWxlbmFtZT1cIlwiIiknIFwKICAtLWFjdGlvbj1kZW55LTQwMyBcCiAgLS1kZXNjcmlwdGlvbj0ndmlydHVhbC1wYXRjaC1jdmUtMjAyNi0xMDk2MTg6IG11bHRpcGFydCBlbXB0eSBmaWxlbmFtZSc=

The oversized check uses a 7+ digit Content-Length regex (1,000,000 bytes or more). Cloud Armor's rules language also supports int(), so this is an equally valid alternative:

CEL
b64:aGFzKHJlcXVlc3QuaGVhZGVyc1snY29udGVudC1sZW5ndGgnXSkgJiYgaW50KHJlcXVlc3QuaGVhZGVyc1snY29udGVudC1sZW5ndGgnXSkgPj0gMTAwMDAwMA==

The regex form is kept as the default because it never has to convert a malformed or attacker-supplied value. Optional canary: add --preview to log only while you review, then remove it.

‍

ModSecurity / OWASP CRS (self-managed, needs SecRequestBodyAccess On)

Load these local rules before the CRS blocking evaluation (for example before REQUEST-949) so the anomaly score counts. Confirm the local rule ID range against your build before deploying.

MODSECURITY
b64:IyBwaHAyc2hlbGwgdmlydHVhbCBwYXRjaCAoTW9kU2VjdXJpdHkgMi54IC8gMy54ICsgQ1JTIDQpLgojIExvYWQgQkVGT1JFIHRoZSBDUlMgYmxvY2tpbmcgZXZhbHVhdGlvbiAoZS5nLiBiZWZvcmUgUkVRVUVTVC05NDkpIHNvIHRoZQojIGFub21hbHkgc2NvcmUgaXMgY291bnRlZC4gUmVxdWlyZXMgU2VjUmVxdWVzdEJvZHlBY2Nlc3MgT24uCgojIFJ1bGUgMTogdXBsb2FkLXByb2dyZXNzIGZpZWxkIG5hbWUuIG1zZy90YWcvc2V2ZXJpdHkgbGl2ZSBvbiB0aGUgU1RBUlRFUi4KU2VjUnVsZSBSRVFVRVNUX01FVEhPRCAiQHN0cmVxIFBPU1QiIFwKICAiaWQ6MTI2MTgsXAogICBwaGFzZToyLFwKICAgYmxvY2ssXAogICBsb2csXAogICBtc2c6J3BocDJzaGVsbDogUEhQX1NFU1NJT05fVVBMT0FEX1BST0dSRVNTIGZpZWxkIChDVkUtMjAyNi0xMDk2MTkpJyxcCiAgIHRhZzonYXR0YWNrLXJjZScsXAogICB0YWc6J3BocDJzaGVsbCcsXAogICB0YWc6J0NWRS0yMDI2LTEwOTYxOScsXAogICBzZXZlcml0eTonQ1JJVElDQUwnLFwKICAgY2hhaW4iCiAgU2VjUnVsZSBBUkdTX05BTUVTfEZJTEVTfE1VTFRJUEFSVF9QQVJUX0hFQURFUlMgXAogICAgIkBjb250YWlucyBQSFBfU0VTU0lPTl9VUExPQURfUFJPR1JFU1MiIFwKICAgICJ0Om5vbmUsdDp1cmxEZWNvZGVVbmksXAogICAgIHNldHZhcjondHguaW5ib3VuZF9hbm9tYWx5X3Njb3JlX3BsMT0rNSciCgojIFJ1bGUgMjogdXJsZW5jb2RlZCBib2R5IGJlZ2lucyB3aXRoICI9IiB0aGVuIGJyYWNrZXQtaGVhdnkgbmFtZXMuClNlY1J1bGUgUkVRVUVTVF9NRVRIT0QgIkBzdHJlcSBQT1NUIiBcCiAgImlkOjEyNjE5LFwKICAgcGhhc2U6MixcCiAgIGJsb2NrLFwKICAgbG9nLFwKICAgbXNnOidwaHAyc2hlbGw6IHVybGVuY29kZWQgZW1wdHktbmFtZSArIGRlZXAgYnJhY2tldHMgKENWRS0yMDI2LTEwOTYxOCknLFwKICAgdGFnOidwaHAyc2hlbGwnLFwKICAgdGFnOidDVkUtMjAyNi0xMDk2MTgnLFwKICAgc2V2ZXJpdHk6J0NSSVRJQ0FMJyxcCiAgIGNoYWluIgogIFNlY1J1bGUgUkVRVUVTVF9IRUFERVJTOkNvbnRlbnQtVHlwZSAiQGNvbnRhaW5zIGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCIgXAogICAgInQ6bG93ZXJjYXNlLFwKICAgICBjaGFpbiIKICBTZWNSdWxlIFJFUVVFU1RfQk9EWSAiQHJ4IF49W149XSo/KD86JTVCfFxbKXszLH0iIFwKICAgICJ0Om5vbmUsdDp1cmxEZWNvZGVVbmksXAogICAgIHNldHZhcjondHguaW5ib3VuZF9hbm9tYWx5X3Njb3JlX3BsMT0rNSciCgojIFJ1bGUgMzogbXVsdGlwYXJ0IGZpbGVuYW1lPSIiIHBsdXMgbmVzdGVkIGZpZWxkIG5hbWVzLiBSRVFVRVNUX0JPRFkgaXMgTk9UCiMgcG9wdWxhdGVkIGZvciBtdWx0aXBhcnQsIHNvIHVzZSBNVUxUSVBBUlRfUEFSVF9IRUFERVJTIC8gRklMRVMuIFRoZQojIGZpbGVuYW1lPSIiIGNoZWNrIGlzIGludGVudGlvbmFsbHkgZXZhbHVhdGVkIHdpdGggdGhlIG5hbWUgY2hlY2s7IHRoZQojIGVtcHR5LWZpbGVuYW1lIHBhcnQgYW5kIHRoZSBicmFja2V0LW5lc3RlZCBuYW1lIGFyZSBzZXBhcmF0ZSBwYXJ0IGhlYWRlcnMuClNlY1J1bGUgUkVRVUVTVF9NRVRIT0QgIkBzdHJlcSBQT1NUIiBcCiAgImlkOjEyNjIwLFwKICAgcGhhc2U6MixcCiAgIGJsb2NrLFwKICAgbG9nLFwKICAgbXNnOidwaHAyc2hlbGw6IGVtcHR5IGZpbGVuYW1lICsgbmVzdGVkIG11bHRpcGFydCBuYW1lcyAoQ1ZFLTIwMjYtMTA5NjE4KScsXAogICB0YWc6J3BocDJzaGVsbCcsXAogICB0YWc6J0dIU0Etdzhody13OWZ4LWo3OXcnLFwKICAgc2V2ZXJpdHk6J0NSSVRJQ0FMJyxcCiAgIGNoYWluIgogIFNlY1J1bGUgUkVRVUVTVF9IRUFERVJTOkNvbnRlbnQtVHlwZSAiQGNvbnRhaW5zIG11bHRpcGFydC9mb3JtLWRhdGEiIFwKICAgICJ0Omxvd2VyY2FzZSxcCiAgICAgY2hhaW4iCiAgU2VjUnVsZSBNVUxUSVBBUlRfUEFSVF9IRUFERVJTICJAcnggKD9pKW5hbWU9XCJbXlwiXSooPzpcW3wlNUIpezMsfSIgXAogICAgInQ6bm9uZSx0OnVybERlY29kZVVuaSxcCiAgICAgY2hhaW4iCiAgU2VjUnVsZSBNVUxUSVBBUlRfUEFSVF9IRUFERVJTICJAcnggKD9pKWZpbGVuYW1lPVwiXCIiIFwKICAgICJ0Om5vbmUi

REQUEST_BODY is not populated for multipart, so the multipart checks use MULTIPART_PART_HEADERS and FILES. If your payloads interleave non-bracket characters between brackets, switch to a counting pattern such as (?:[^[]*\[){3,}.

‍

Patch, verify, then remove the broad rule

  1. Install PHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 from php.net/downloads or a trusted distributor (on 7.4, 8.0 or 8.1, a build from PHP's official source backports) on every public origin, and restart every PHP worker.
  2. Verify the installed version and package checksums on every origin.
  3. Hunt for compromise: multipart requests followed by memory-limit errors or repeated 500s, "Allowed memory size ... exhausted ... in Unknown on line 0", AH00052 segfault bursts, PHP workers spawning shells, new admin users, unexpected PHP files, and anomalous outbound connections.
  4. Only then remove the broad custom rules (AWS A1 to A3, Azure, Google Cloud Armor, F5, ModSecurity and Imperva customs). Narrow any you keep to exact endpoints.
  5. Leave vendor managed and response-side rules (Cloudflare Managed and Response Firewall, Akamai ASE 3000991, Fastly templated rule, Imperva built-in protection) enabled unless the vendor documents they are obsolete after the patch.
  6. Confirm no bypassable direct-to-origin path survives (origin IP, alternate hostname, legacy DNS).

‍

How we built the virtual patch

Research motivation

Before PHP published technical details, our goal was a rule narrow enough to stay in Block mode across thousands of applications, and portable enough to deploy on every major edge vendor.

Finding the narrowest trigger

Every circulating PoC we reviewed before the release, and every one PenTestIT examined, sent a multipart POST with a form field named PHP_SESSION_UPLOAD_PROGRESS. That field activates PHP's upload-progress handling, a feature PHP itself describes as "hardly-used" (PHP advisory). Blocking it shuts the session upload-progress path, one confirmed path of CVE-2026-109619, without affecting ordinary file uploads. It is not complete coverage: Fastly documents other exploit shapes, such as urlencoded bodies over 1 to 2 MB and bodies that begin with = followed by deeply bracketed field names (Fastly), and the out-of-memory path of CVE-2026-109618 does not depend on the upload-progress setting. The rule pack adds heuristic rules for those shapes where a vendor can express them. Patching remains the only complete fix.

Parser differentials

If a WAF and PHP parse the same request differently, an attacker can use the gap to bypass the rule.

  • Content-Type. PHP lowercases the header and cuts it at the first ;, , or space before checking for multipart/form-data. An exact or case-sensitive header match can miss variants PHP accepts, so the rule matches the field in the body instead.
  • Field-name syntax. We match after URL decoding and lowercasing, and cover both quote styles around the field name.
  • Duplicate Names. When a part has more than one name= parameter, PHP uses the last one. A WAF that parses field names and keeps the first can be bypassed. A plain body-string match is not affected.
  • Body size. The field only has to appear before a file part, so an attacker can put padding fields in front of it to push it past the WAF's inspection limit. Raise body-inspection limits where possible, and rely on session.upload_progress.enabled = Off at the origin for anything larger.
  • Custom names. Origins that changed session.upload_progress.name need the rule to match that name instead.

What we rejected

‍

  • Blocking all multipart. It breaks logins, checkouts and media uploads. We keep it only as a scoped fallback where a WAF cannot inspect bodies.
  • Blocking PHPSESSID cookies. Some mitigations shared online do this. It breaks nearly every PHP application.
  • Fingerprints from unverified tarballs. Building rules from contested exploit code risks both false confidence and false positives.
  • User-Agent matching. Scanner strings change trivially and real attackers usually won't announce themselves.

‍

Conclusions

php2shell is a reminder that much of the web runs on a layer most teams never look at. A bug in PHP core reaches every framework and CMS on top of it, and about a third of PHP sites still run end-of-life major versions (W3Techs).

Patching is the only complete fix, but it is not instant. Exploitation attempts began before the release, upgrades take days on managed hosting, and end-of-life fleets depend on source backports or distributor builds, with nothing at all for PHP 7.3 and older. The edge is the only layer you can change everywhere, today, without touching application code.

Finally, a virtual patch is only as good as its coverage. A rule that is live on one CDN but not on a load balancer, or stuck in Log mode, protects no one. The real work is keeping coverage consistent across vendors, which is why we treat virtual patching as an orchestration problem.

‍

Timeline

All times UTC, October 21, 2026.

  • Before release: watchTowr reports reproducing CVE-2026-109618 (SecurityWeek).
  • 03:02: Cloudflare managed rules for php2shell are live by default on free and paid WAF plans, in Block (Cloudflare).
  • 07:25: Huskeys delivers the interim virtual patch to customers.
  • 13:30: PHP releases 8.5.12, 8.4.27, 8.3.36 and 8.2.35, plus source backports for 7.4, 8.0 and 8.1, a day earlier than originally planned (CERT-EU, PHP Foundation).
  • 13:48: The CVE-2026-109618 record is published. NVD publishes CVE-2026-109619 at 14:17.
  • Same day: Akamai ships Adaptive Security Engine rule 3000991 v1 naming both CVEs (Akamai), Fastly ships a CVE-specific virtual patch for customers to enable (Fastly), Imperva publishes guidance that Cloud WAF customers are protected out of the box (Imperva), and CISA urges immediate patching (CISA). AWS Managed Rules has no php2shell rule (AWS changelog).

‍

How can Huskeys help?

Huskeys sits on top of the edge stack you already run (CDNs, WAFs, load balancers, security groups) and coordinates it, rather than replacing it (Huskeys). For php2shell, Huskeys customers can:

  • Deploy one virtual patch to every vendor. The interim rule is translated into each connected WAF and CDN's syntax and pushed everywhere at once, so no edge is left on yesterday's policy.
  • Find bypassable origins. Huskeys maps every ingress path to PHP and flags load balancers, origin IPs and hostnames the virtual patch doesn't cover.
  • Get vendor rules into Block. Cloudflare's managed rules, Akamai's Adaptive Security Engine rule 3000991, Fastly's virtual patch and Imperva's built-in protection have shipped. Huskeys confirms each one is in Block, not Log, and removes overrides that quietly downgrade them.
  • Scope fallbacks with Profiling. Huskeys Profiling maps which parts of your web applications the business depends on (SiliconANGLE), so any multipart fallback lands only where it can't break customers.

Iteration matters: during React2Shell, Vercel shipped 20 WAF updates in the first 48 hours after its bounty program launched, as researchers found bypasses (Vercel). A virtual patch that can't change that fast is a snapshot, not a control.

‍

Sources

Share this blog

The edge moves fast.
Stay ahead with Huskeys.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Read this next

When a 404 Turns Into a Jackpot

11 Nov 2025
Huskeys Team

It Looks Like an Attack

08 Oct 2026
Yarin Yolles

Stopping React's Perfect-10 RCE Before It Stops You

04 Dec 2025
Huskeys Team