
Comparing the AWS WAF Managed Rule PHP2Shell_BODY with a Custom Rule and Testing It with CloudFront's 64 KB Body Inspection
This page has been translated by machine translation. View original
Introduction
On October 21, 2026, PHP2Shell_BODY was added to the AWS-provided managed rule group for PHP (AWSManagedRulesPHPRuleSet).
In a previous article, I verified the custom rules PHP2Shell and block-oversize-body, based on the AWS security bulletin 2026-143-AWS, using AWS WAF associated with an ALB.
In this article, I compare the requests blocked by the custom rule PHP2Shell and by the managed rule PHP2Shell_BODY.
I also set the body inspection size limit to 64 KB on CloudFront, and share the results of measuring how far into the body the inspection of string-matching rules reaches.
Overview of PHP2Shell_BODY
PHP2Shell_BODY is a rule that detects requests corresponding to CVE-2026-109619. The WCU of the rule group AWSManagedRulesPHPRuleSet is 100, and the rule's label is awswaf:managed:aws:php-app:PHP2Shell_Body. The detection conditions (strings, method, Content-Type) are not described in the documentation.
In the console as of October 23, 2026, the default version is 2.5. I ran the verification with the version pinned to 2.4.

Checking the version
The bulletin explains that WebACLs using the default version are automatically protected by the rule group update, while WebACLs with a pinned version need to be updated to the latest version. PHP2Shell_BODY is included in Version_2.3 and later, and PHP2Shell_BODY_Extended is included in Version_2.4 and later.
The following table is the list of versions obtained in us-east-1 at 13:47 on 2026-10-23.
| Version | Release date and time (JST, CLOUDFRONT scope) | changelog date | PHP2Shell_BODY | PHP2Shell_BODY_Extended | Changes (changelog) |
|---|---|---|---|---|---|
| Version_2.5 (current default) | 2026-10-23 09:25 | 2026-10-22 | Yes | Yes | Improved detection signatures of PHP2Shell_BODY_Extended (CVE-2026-109618, CVE-2026-109619) |
| Version_2.4 | 2026-10-22 19:39 | 2026-10-22 | Yes | Added | Added PHP2Shell_BODY_Extended. Directly detects CVE-2026-109618, replacing the guidance in 2.3 (SizeRestrictions_BODY of the CRS) |
| Version_2.3 | 2026-10-22 09:29 | 2026-10-21 | Added | No | Added PHP2Shell_BODY (CVE-2026-109619). For CVE-2026-109618, guided protection via SizeRestrictions_BODY of the CRS (Block bodies over 8 KB) |
| Version_2.2 | March 2026 | March 2026 | No | No | Improved detection, added PHPHighRiskMethodsVariables_URIPATH |
| Version_2.1 | July 2024 | July 2024 | No | No | Added JS_DECODE to PHPHighRiskMethodsVariables_BODY and _QUERYSTRING |
| Version_2.0 | February 2023 | February 2023 | No | No | Improved signatures of all rules. Replaced _QUERYARGUMENTS with _QUERYSTRING and added _HEADER. Changed label names |
| Version_1.1 | October 2021 | Not listed | No | No | Not listed in the changelog |
| Version_1.0 | October 2021 | Not listed | No | No | Not listed in the changelog |
You can check the version you are using in the "Version" field of the console. If you have pinned it, it is also included in Rules[].Statement.ManagedRuleGroupStatement.Version in the get-web-acl response. To remove the pin and follow the default version, select the default in the "Version" field.
The published versions are included in Versions of the list-available-managed-rule-group-versions response, and the default version is in CurrentDefaultVersion.
aws wafv2 list-available-managed-rule-group-versions \
--name AWSManagedRulesPHPRuleSet \
--vendor-name AWS \
--scope CLOUDFRONT \
--region us-east-1
For REGIONAL WebACLs such as ALB, run it with --scope REGIONAL --region <WebACL's region>. Release dates differ by scope, so check with the scope you are using.
Configuring the WebACL and WAF logs
Besides PHP2Shell_BODY, the rule group has 4 PHPHighRiskMethodsVariables_* rules and PHP2Shell_BODY_Extended for CVE-2026-109618. The default action of all 6 rules is Block. In the verification, I set only PHP2Shell_BODY to Block and overrode the other 5 rules to Count. The element added to the WebACL's Rules in CloudFormation is as follows.
- Name: AWSManagedRulesPHPRuleSet
Priority: 10
OverrideAction:
None: {}
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesPHPRuleSet
Version: Version_2.4
RuleActionOverrides:
- Name: PHP2Shell_BODY
ActionToUse:
Block: {}
- Name: PHP2Shell_BODY_Extended
ActionToUse:
Count: {}
- Name: PHPHighRiskMethodsVariables_HEADER
ActionToUse:
Count: {}
- Name: PHPHighRiskMethodsVariables_QUERYSTRING
ActionToUse:
Count: {}
- Name: PHPHighRiskMethodsVariables_BODY
ActionToUse:
Count: {}
- Name: PHPHighRiskMethodsVariables_URIPATH
ActionToUse:
Count: {}
VisibilityConfig:
CloudWatchMetricsEnabled: true
SampledRequestsEnabled: true
MetricName: AWSManagedRulesPHPRuleSet
Version_2.4 is the value I pinned for the verification. If you do not specify Version, the default version is used, and rule updates by AWS are reflected automatically. If you specify Version, it is pinned to that version, and subsequent updates are not reflected.
If you set PHP2Shell_BODY to Count, you can record matches in the WAF logs without blocking requests. The following is an excerpt of the relevant part from the WAF log of a configuration with all rules set to Count on an ALB. The log destination is CloudWatch Logs, and the log group name must start with aws-waf-logs-.
{
"action": "ALLOW",
"terminatingRuleId": "Default_Action",
"ruleGroupList": [
{
"ruleGroupId": "AWS#AWSManagedRulesPHPRuleSet#Version_2.4",
"nonTerminatingMatchingRules": [
{
"ruleId": "PHP2Shell_BODY",
"action": "COUNT",
"overriddenAction": "BLOCK",
"ruleMatchDetails": []
}
]
}
],
"labels": [
{
"name": "awswaf:managed:aws:php-app:PHP2Shell_Body"
}
]
}
PHP2Shell_BODY_Extended did not match on either ALB or CloudFront with the harmless dummy requests I sent this time.
Comparison with the custom rules
Configurations tested
Since the detection conditions of PHP2Shell_BODY are not public, the definitions cannot be compared side by side. So I sent requests of the same shape to a configuration with the two custom rules from the previous article (PHP2Shell and block-oversize-body) set to Block, and to a configuration with only PHP2Shell_BODY set to Block, and compared the actual behavior. In addition to 55 cases common to ALB and CloudFront, I sent 22 cases to CloudFront near the boundary of the inspection limit and by body size. Two CloudFront cases of GET with a body are excluded from the comparison because CloudFront returns 403 before WAF.
The environments tested were as follows.
- ALB: Tokyo region. Body inspection limit fixed at 8 KB
- CloudFront: WebACL of the CLOUDFRONT scope in us-east-1. Body inspection limit set to 64 KB
- Rule group: pinned to Version_2.4
- Only the measurement of PHP2Shell_BODY with a 16 KB limit used a configuration with PHP2Shell_BODY_Extended also set to Block
Requests were identified by the x-case header. The blocking rule was confirmed with the response header x-waf-blocked-by, attached via a Block custom response, and the WAF logs.
Everything I sent was a harmless dummy. The origin returned a fixed response, and I judged by the response status code.
The custom rule PHP2Shell matches requests where the method is POST, PHPSESSID is in a cookie key or the body, and PHP_SESSION_UPLOAD_PROGRESS is in the body. block-oversize-body matches when the body is larger than the threshold, and OversizeHandling is MATCH.
Definition of the custom rule PHP2Shell (CloudFormation YAML)
- Name: block-php-upload-progress
Priority: 4
Action:
Block: {}
Statement:
AndStatement:
Statements:
- ByteMatchStatement:
SearchString: POST
FieldToMatch:
Method: {}
TextTransformations:
- Priority: 0
Type: NONE
PositionalConstraint: EXACTLY
- OrStatement:
Statements:
- ByteMatchStatement:
SearchString: PHPSESSID
FieldToMatch:
Cookies:
MatchPattern:
All: {}
MatchScope: KEY
OversizeHandling: CONTINUE
TextTransformations:
- Priority: 0
Type: NONE
PositionalConstraint: EXACTLY
- ByteMatchStatement:
SearchString: PHPSESSID
FieldToMatch:
Body:
OversizeHandling: CONTINUE
TextTransformations:
- Priority: 0
Type: NONE
PositionalConstraint: CONTAINS
- ByteMatchStatement:
SearchString: PHP_SESSION_UPLOAD_PROGRESS
FieldToMatch:
Body:
OversizeHandling: CONTINUE
TextTransformations:
- Priority: 0
Type: NONE
PositionalConstraint: CONTAINS
VisibilityConfig:
CloudWatchMetricsEnabled: true
SampledRequestsEnabled: true
MetricName: block-php-upload-progress
Do they block the same requests?
The custom column is the configuration with the two rules PHP2Shell and block-oversize-body set to Block, and the managed column is the configuration with only PHP2Shell_BODY set to Block. The threshold of the custom block-oversize-body is 8,192 for both ALB and CloudFront. All values in the table are results from a single measurement each.
| Request | ALB custom | ALB managed | CloudFront (64 KB) custom | CloudFront (64 KB) managed |
|---|---|---|---|---|
| POST, PHPSESSID in cookie, PHP_SESSION_UPLOAD_PROGRESS in body | 403 PHP2Shell | 403 PHP2Shell_BODY | 403 PHP2Shell | 403 PHP2Shell_BODY |
| POST, only PHP_SESSION_UPLOAD_PROGRESS in body | 200 | 200 | 200 | 200 |
| POST, JSON body with PHPSESSID and PHP_SESSION_UPLOAD_PROGRESS | 403 PHP2Shell | 403 PHP2Shell_BODY | 403 PHP2Shell | 403 PHP2Shell_BODY |
| POST reproducing the order of the public PoC in harmless form (about 1 MB) | 403 oversize | 200 | 403 PHP2Shell | 403 PHP2Shell_BODY |
Looking at all cases, the set of requests that PHP2Shell_BODY returned 403 for matched the set that the custom PHP2Shell returned 403 for in each environment (26 for ALB, 41 for CloudFront). Requests with both PHPSESSID and PHP_SESSION_UPLOAD_PROGRESS in a JSON, text/plain, no-Content-Type, or urlencoded body were blocked in both configurations. Requests with the PUT or PATCH method, requests with lowercase field names, and requests containing only one of the two passed in both configurations.
8 KB on ALB and 64 KB on CloudFront
Specification of the body inspection limit
The body inspection limit is the value that determines how many bytes from the start of the body WAF inspects. For ALB it is fixed at 8 KB (8,192 bytes). For CloudFront the default is 16 KB (16,384 bytes), and it can be set in 16 KB steps to 32 KB, 48 KB, or 64 KB.
Setting up to 64 KB on CloudFront became possible in April 2023.
For CloudFront, specify one of KB_16, KB_32, KB_48, or KB_64 in DefaultSizeInspectionLimit in the WebACL's AssociationConfig.
AssociationConfig:
RequestBody:
CLOUDFRONT:
DefaultSizeInspectionLimit: KB_64
Inspecting beyond the default 16 KB incurs an additional charge. As of October 23, 2026, the unit price is $0.30 per 1 million requests for each 16 KB beyond 16 KB, and it applies only to the inspection of requests whose body exceeds 16 KB.
The range blocked by string-matching rules at 64 KB on CloudFront
The boundary at the first 8 KB on ALB shown in the previous article was the same for PHP2Shell_BODY. Requests with PHP_SESSION_UPLOAD_PROGRESS beyond 8 KB passed with 200 in the configuration with only PHP2Shell_BODY. In the custom configuration, block-oversize-body (threshold 8,192) returned 403. The order in the public PoC has this shape too. After 1,001 dummy fields come two fields, PHPSESSID and PHP_SESSION_UPLOAD_PROGRESS (hereafter, the reserved fields), followed by a large field. The reserved fields are at a position about 56 KB from the start. I reproduced only the order and sizes of the field names, and did not run the PoC.
On CloudFront with the body inspection limit set to 64 KB, both PHP2Shell_BODY and the custom PHP2Shell returned 403 for a request with the reserved fields at this position.
| Request | PHP2Shell_BODY only (no size rule) | Custom PHP2Shell + block-oversize-body (threshold 65,536) |
|---|---|---|
| PHP_SESSION_UPLOAD_PROGRESS ends at byte 32,000 (body 33,073 bytes) | 403 PHP2Shell_BODY | 403 PHP2Shell |
| Public PoC order with the large field removed (body 56,335 bytes) | 403 PHP2Shell_BODY | 403 PHP2Shell |
| PHP_SESSION_UPLOAD_PROGRESS ends at byte 65,537 (body 66,610 bytes) | 200 | 403 oversize |
| Legitimate POST, 20 KB JSON | 200 | 200 |
| Legitimate POST, 65,536 bytes | 200 | 200 |
| Legitimate POST, 65,537 bytes | 200 | 403 oversize |
| Legitimate POST, 100 KB image upload | 200 | 403 oversize |
The request that ends at byte 65,537 passed the string-matching rules because PHP_SESSION_UPLOAD_PROGRESS is outside the inspection limit. In the configuration with a threshold of 65,536, block-oversize-body blocked it.
The following table shows, for each inspection limit, up to which byte from the start the end of PHP_SESSION_UPLOAD_PROGRESS could be for the string-matching rule to match. The custom PHP2Shell column is the position where PHP2Shell matched.
| Configuration | Maximum position where PHP2Shell_BODY matched (byte) | Maximum position where custom PHP2Shell matched (byte) |
|---|---|---|
| ALB (limit fixed at 8 KB) | 8,192 | 8,192 |
| CloudFront (limit 16 KB) | 16,384 | 16,384 |
| CloudFront (limit 64 KB) | 65,536 | 65,536 |
In the custom configuration where the limit stayed at 16 KB and only the block-oversize-body threshold was set to 65,536, block-oversize-body returned 403 for POSTs of 16,385 bytes or more. This is because, when OversizeHandling is MATCH, a body exceeding the inspection limit is judged as a match regardless of the threshold.
The position of the reserved fields is determined by the size of the dummy field placed before them. The following is an example that places the end of PHP_SESSION_UPLOAD_PROGRESS at byte 32,000.
# PHP_SESSION_UPLOAD_PROGRESS の終端が N+187 バイト目になる POST(N=31813 で 32,000 バイト目)
# 表のリクエストは、この後ろに約 1,000 バイトのフィールドを追加したもの
mkpart() { printf -- '--x\r\nContent-Disposition: form-data; name="%s"\r\n\r\n%s\r\n' "$1" "$2"; }
fill() { head -c "$1" /dev/zero | tr '\0' 'a'; }
N=31813
{ mkpart pad "$(fill "$N")"; mkpart PHPSESSID dummy; mkpart PHP_SESSION_UPLOAD_PROGRESS dummy; printf -- '--x--\r\n'; } > f.bin
curl -s -o /dev/null -w '%{http_code}\n' -X POST "https://<ホスト名>/" \
-H 'Content-Type: multipart/form-data; boundary=x' \
--data-binary @f.bin
For the POSTs by size, I created bodies of the specified number of bytes as follows and sent them.
# 65,536 バイトの POST
head -c 65536 /dev/zero | tr '\0' 'a' > body.bin
curl -s -o /dev/null -w '%{http_code}\n' -X POST "https://<ホスト名>/" \
-H 'Content-Type: application/octet-stream' \
--data-binary @body.bin
Summary
PHP2Shell_BODY, added to the AWS WAF PHP rule group (AWSManagedRulesPHPRuleSet), blocked the same requests as the custom rule PHP2Shell from the previous article on both ALB and CloudFront.
PHP2Shell_BODY is easier to adopt than writing a custom rule, and you can also expect AWS to tune the rule. Since tuning is reflected in WebACLs that use the default version, please check this if you have pinned the version.
The body inspection limit is fixed at 8 KB on ALB, and up to 64 KB (default 16 KB) on CloudFront. Blocking requests that exceed the limit with block-oversize-body is thought to reduce the PHP2Shell risk, but it may also block legitimate requests from forms and uploaders.
When adopting it with Block, I recommend recording WAF logs and checking that no unintended blocking occurs. Also, a properly configured WAF can be expected to have some effect in mitigating the risk of PHP vulnerabilities, but it is not a complete countermeasure. I recommend updating PHP to the fixed versions (8.5.12, 8.4.27, 8.3.36, 8.2.35 or later for each series) as soon as possible.
