If you run a public PHP application and cannot schedule an upgrade before the weekend, deploy the edge rule now and accept that your upload-progress bars will break until the patch lands. That is the short version. On October 21, 2026, PHP shipped an emergency release fixing two critical remote code execution flaws in session upload progress handling, and Amazon Threat Intelligence had already observed an exploitation attempt on October 17, four days before public disclosure, per Tenable’s FAQ on the php2shell vulnerabilities. AWS responded with security bulletin 2026-143, which publishes a ready-made WAF rule named PHP2Shell. The rule is unusually well-aimed: its match conditions are exactly the preconditions the vulnerable code requires, which independent source analysis confirms. But it has a cost the bulletin does not mention, and it does not cover every attack chain. This article translates the rule into plain match logic, prices the tradeoff, and lays out how to detect attempts and retire the block once you upgrade.
Why the patch window is the real problem
The two CVEs at the center of this are CVE-2026-109618, an out-of-memory corruption issue, and CVE-2026-109619, a stale global state and use-after-free flaw in PHP’s session upload progress handler. PHP’s security team rates both at CVSS v4.0 9.2 and says each can allow unauthenticated remote code execution, according to Tenable’s writeup. The same source reports that php2shell is now being broadly exploited in the wild. CVSS scores differ across sources, so treat 9.2 as the PHP team’s rating rather than a settled consensus number.
The exposure is wider than many teams assume, because the vulnerable feature is on by default. Datadog Security Labs’ analysis states that PHP enables session upload progress by default and that it runs before the target script executes, even if the application never calls session_start(). The affected server API inventory covers mod_php, PHP-FPM, php-cgi, FrankenPHP, and LiteSpeed. Fixed versions are PHP 8.2.35, 8.3.36, 8.4.27, and 8.5.12, with source backports published for end-of-life branches 7.4, 8.0, and 8.1, per Datadog.
Updating is the only complete fix. Both Tenable and Datadog are explicit on this point; Datadog’s phrasing is that mitigations circulating in the community “don’t fully protect you.” So the question is not whether to patch, but what to do during the days or weeks between now and your maintenance window. That gap is what a virtual patch fills.
The AWS PHP2Shell rule in plain language
AWS security bulletin 2026-143 ships a custom WAF rule named PHP2Shell targeting CVE-2026-109619. Reading the JSON, the rule blocks a request only when all of the following hold:
- The HTTP method is exactly POST.
- The string
PHPSESSIDappears either as a cookie key (match scope KEY, across all cookies) or in the request body. - The string
PHP_SESSION_UPLOAD_PROGRESSappears in the request body.
The action is Block. All matches use PositionalConstraint CONTAINS or EXACTLY with no text transformations (Type: NONE), and oversize handling is set to CONTINUE on body and cookie inspections, meaning the WAF still inspects oversized payloads rather than skipping them. Visibility is enabled: sampled requests and CloudWatch metrics under the metric name PHP2Shell, per the bulletin’s VisibilityConfig block.
Why this conjunction and not something broader? Because it mirrors the trigger precondition of the bug itself. RemiPelloux’s independent source-level analysis of the vulnerable rfc1867 handler puts it directly: “The request must carry both a session id (cookie or PHPSESSID field) and the progress field, before the file part. Miss either and nothing happens at all.” The AWS rule matches precisely that conjunction. A request missing either field cannot reach the vulnerable code path, so blocking requests that carry both is a tight virtual patch rather than a blunt one. This is the part of the story the advisory coverage skipped: the rule is not a heuristic guess, it is a transcription of the bug’s own entry conditions into WAF logic.
Porting the signature to other WAF stacks
If you are not on AWS WAF, the signature ports cleanly as a logical AND of three conditions. Datadog notes that “controls at a reverse proxy or another boundary in front of PHP can block requests before they reach the vulnerable parser,” which is the general principle behind any port.
For ModSecurity or a compatible engine, express it as a chained rule: match method POST, then require PHPSESSID in request cookies or the request body, then require PHP_SESSION_UPLOAD_PROGRESS in the body, action deny. For nginx, the 0x4148 lab’s defense-in-depth notes suggest keeping fastcgi_request_buffering enabled so the full request body is buffered before proxying, per the CVE-2026-109618 php2shell lab; a Lua or njs script can then apply the same three-condition check. Wherever you port it, preserve two properties from the AWS original: inspect the body even when oversized (the CONTINUE behavior), and emit a dedicated metric so you can count hits separately from your generic WAF noise.
One porting caution deserves emphasis. The AWS rule applies no text transformations, so case-mutated or encoded field names may not match [unverified]. More fundamentally, the WAFFLED paper on WAF evasion (arXiv:2503.10846) shows that rule-based WAFs can be bypassed through parsing discrepancies between the WAF and the backend, “not… obfuscating the payload itself, but… how WAFs interpret HTTP requests before applying their rulesets.” If your WAF parses multipart bodies differently than PHP does, an attacker may be able to construct a request your WAF sees as benign and PHP sees as a match. The supplied evidence does not test whether any specific discrepancy defeats this particular rule, so treat the port as a strong filter, not a guarantee, and verify with your own test requests before trusting it.
The tradeoff the bulletin omits: your progress bars match the same signature
Here is the cost AWS’s bulletin does not discuss. The conjunction the rule blocks, POST plus a PHPSESSID cookie plus PHP_SESSION_UPLOAD_PROGRESS in the body, is exactly what a browser sends when an application legitimately uses PHP’s upload progress feature to render a progress bar. That is not a measured false-positive rate; it is a bounded inference from the rule semantics and the documented trigger precondition. No supplied source quantifies how much legitimate traffic the rule drops in the wild. But if your application, or a plugin in it, implements upload progress, that feature will stop working the moment you deploy the block.
I would deploy it anyway, with one condition: check first whether anything you ship actually uses the feature. In my reading of the evidence, the risk asymmetry favors blocking, because exploitation is reportedly active and the vulnerable handler runs by default, per Datadog. But the honest framing is that this rule shifts the burden from patch scheduling to edge policy. You are buying time for your maintenance window by temporarily disabling a PHP feature at the boundary, and the price of that time is any user-facing progress indication that depends on it. If a broken progress bar on a high-traffic upload flow is unacceptable, the alternative is the config-level mitigation below, which has the same feature cost but applies server-side.
Exposure triage before you deploy
Work through these checks in order; each one changes what you should do.
| Check | What to look for | What it changes |
|---|---|---|
| PHP version | Anything below 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 | Unpatched: you need a compensating control now. One analysis claims every version from 5.4.0 to 8.5.11 is affected, but that comes from a single researcher and is [unverified] |
| SAPI in use | mod_php, PHP-FPM, php-cgi, FrankenPHP, LiteSpeed | All are in Datadog’s affected inventory; mod_php specifically changes your coverage calculus (see below) |
session.upload_progress.enabled | Default is enabled, and it runs pre-script even without session_start() | If you can set it to 0, do so; PHP’s advisory lists this as exposure-reducing for CVE-2026-109619 |
| File uploads + sessions in the app | Multipart POST endpoints reachable unauthenticated | These are the paths the AWS rule protects; prioritize them in WAF scope |
| Upload-progress UI in use | Any feature relying on PHP_SESSION_UPLOAD_PROGRESS | This is your false-positive surface; expect it to break under the block |
Two compensating controls sit alongside the edge rule, with different coverage and cost:
| Control | Coverage | Cost | Residual risk |
|---|---|---|---|
| AWS PHP2Shell rule (or ported equivalent) | The session-upload-progress trigger conjunction, per the AWS bulletin and RemiPelloux’s analysis | Breaks legitimate upload-progress traffic; WAF parsing discrepancies may allow evasion | Other chains, including the mod_php OOM path, remain open |
session.upload_progress.enabled=0 (plus fatal_error_backtraces=0 on PHP 8.5) | Reduces exposure to CVE-2026-109619 paths, per Tenable | Same feature loss; requires restarting every PHP worker | PHP states updating is the only complete fix |
| Upgrade to 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 | All known chains | Maintenance window, regression testing | None documented in the supplied evidence |
The sensible combination is rule plus config mitigation now, upgrade as soon as the window allows.
What the rule cannot cover
Two gaps matter, and both argue against treating the edge block as sufficient.
First, the mod_php OOM chain. One researcher’s dissection of the vulnerabilities describes a “Chain D (OOM): Targets any Apache mod_php installation. No application dependency at all,” a runtime-level path that requires no session ID and no PHP_SESSION_UPLOAD_PROGRESS field. A body signature keyed on those fields cannot see it. Treat this as a single-researcher claim: the same writeup’s broader assertions (five independent RCE chains, the 5.4.0-to-8.5.11 range) partly conflict with RemiPelloux’s repository, which reports failing to reproduce code execution for its mechanism and labels that RCE claim “retracted — memory safety only.” The safe reading is that a session-field signature cannot be assumed to cover chains that need no session fields, and if you run mod_php, the upgrade is more urgent for you than for a PHP-FPM shop behind a good WAF.
Second, evasion of the body match itself. The 0x4148 lab demonstrates a stall-based use-after-free trigger: a multipart POST with a session ID and PHP_SESSION_UPLOAD_PROGRESS, sent in two bursts separated by a pause longer than session.upload_progress.min_freq (1 second by default), per the lab’s writeup. That path still uses the same field names, so the rule catches it; timing is not the gap. The open question is whether renaming or omitting fields could still trigger the bug while slipping the match. RemiPelloux’s analysis suggests missing either field means “nothing happens at all,” which cuts against trivial evasion, but no supplied source tests mutation of the field names directly, so evasion remains [unverified]. Combined with the WAFFLED parsing-discrepancy results, the right posture is: the rule covers the documented trigger shape well, and you should not assume it covers shapes nobody has published yet.
Detecting attempts while the rule holds
You get telemetry for free if you deploy the AWS version: the rule emits sampled requests and a CloudWatch metric named PHP2Shell, per the bulletin. A rising count on that metric before your patch lands is your exploitation-attempt signal; a flat zero combined with user complaints about broken uploads is your false-positive signal. Watch both.
On the host side, Datadog’s detection guidance points to three log patterns: unusually large form submissions, memory-exhaustion errors during request initialization, and unexpected shell processes spawned from PHP workers. One subtlety worth internalizing: successful command execution does not necessarily crash the worker, so the absence of crashes is not evidence of absence. If your WAF metric shows blocked attempts and your PHP error log shows memory exhaustion on the same endpoints, assume the rule is catching real probes and accelerate the upgrade.
Retiring the block
Treat the edge rule as debt with a known payoff date. Once your fleet is on 8.2.35, 8.3.36, 8.4.27, or 8.5.12 (or a backported build for 7.4 through 8.1, per Datadog), roll the rule from Block to Count, confirm legitimate upload-progress traffic flows again, then delete it. Keep the config mitigation if it costs you nothing, but do not let either control linger as a substitute for the patch; PHP’s own advisory says configuration changes “reduce exposure to specific attack paths but do not make an installation safe,” per Tenable.
The durable lesson outlasts this CVE. A virtual patch is worth deploying when three things line up: exploitation is already observed, the signature sits exactly on the bug’s entry conditions rather than approximating them, and the false-positive cost is a feature you can live without for days. All three hold here. What the supplied evidence cannot settle is the rule’s measured false-positive rate, its real-world evasion resistance, and coverage of the chains that need no session fields at all. Deploy the block, restart your workers with upload progress disabled if you can, watch the PHP2Shell metric, and book the upgrade window anyway. The rule buys you the time to patch calmly; it does not buy you the option not to patch.
Frequently Asked Questions
Which PHP versions fix the php2shell vulnerabilities?
Fixed versions are PHP 8.2.35, 8.3.36, 8.4.27, and 8.5.12, with source backports published for end-of-life branches 7.4, 8.0, and 8.1, per Datadog.
What legitimate feature breaks when you deploy the PHP2Shell WAF rule?
The conjunction the rule blocks, POST plus a PHPSESSID cookie plus PHP_SESSION_UPLOAD_PROGRESS in the body, is exactly what a browser sends when an application legitimately uses PHP’s upload progress feature to render a progress bar.
How do you retire the PHP2Shell block after upgrading?
Once your fleet is on 8.2.35, 8.3.36, 8.4.27, or 8.5.12 (or a backported build for 7.4 through 8.1, per Datadog), roll the rule from Block to Count, confirm legitimate upload-progress traffic flows again, then delete it.
Join the discussion
Share a useful perspective or ask a question about this article.