Comparing the AWS WAF Managed Rule PHP2Shell_BODY with a Custom Rule and Testing It with CloudFront's 64 KB Body Inspection

Comparing the AWS WAF Managed Rule PHP2Shell_BODY with a Custom Rule and Testing It with CloudFront's 64 KB Body Inspection

AWS WAF managed rule PHP2Shell_BODY blocked the same requests as the custom rule PHP2Shell. With CloudFront's body inspection limit at 64 KB, it caught strings past the 8 KB ALB limit, with no size rule needed.
2026.10.23

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).

https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-changelog.html

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.

https://dev.classmethod.jp/articles/aws-security-bulletin-php2shell-waf-rules/

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.

https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-use-case.html

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.

Screen of AWS-AWSManagedRulesPHPRuleSet in the AWS WAF console. The version is "Default (uses Version_2.5)", the capacity is 100, and the actions of all 6 rules are Block

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.

https://aws.amazon.com/security/security-bulletins/2026-143-aws/

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.

https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-setting-body-inspection-limit.html

Setting up to 64 KB on CloudFront became possible in April 2023.

https://aws.amazon.com/about-aws/whats-new/2023/04/aws-waf-body-inspections-amazon-cloudfront-distributions/

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.

https://aws.amazon.com/waf/pricing/

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.

Share this article

AWSのお困り事はクラスメソッドへ