I tried out the AWS WAF rules introduced in the AWS Security Bulletin (2026-143-AWS) and the AWS Security Blog to mitigate PHP2Shell

I tried out the AWS WAF rules introduced in the AWS Security Bulletin (2026-143-AWS) and the AWS Security Blog to mitigate PHP2Shell

Two AWS WAF rules (PHP2Shell and Block-Oversize-Body) mitigating CVE-2026-109618 and CVE-2026-109619 were added, verified with curl, and false positive side effects were confirmed.
2026.10.22

This page has been translated by machine translation. View original

Introduction

For two PHP vulnerabilities (commonly known as PHP2Shell: CVE-2026-109618 and CVE-2026-109619), AWS Security Bulletin 2026-143-AWS provides information on affected PHP versions and fixed versions, as well as AWS WAF rules as an interim mitigation for cases where immediate updates are not possible.

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

The AWS Security Blog also introduces mitigation measures using AWS WAF.

https://aws.amazon.com/jp/blogs/security/using-aws-waf-to-apply-mitigations-for-php2shell-rce-vulnerabilities-cve-2026-109618-and-cve-2026-109619/

This article presents the results of applying the two rules introduced as PHP2Shell mitigations (PHP2Shell and Block-Oversize-Body) to a test ALB and verifying the following three points using curl.

  • The difference between requests that are blocked and those that pass through
  • The result when PHP_SESSION_UPLOAD_PROGRESS is located after the first 8 KB of the body
  • The conditions under which non-attack requests are blocked

The Two Rules Provided by AWS

The AWS Security Bulletin lists one rule per CVE as an interim mitigation for those using AWS WAF. The custom rule PHP2Shell is for CVE-2026-109619, and SizeRestrictions_BODY, included in the AWS Managed Rules Core Rule Set (CRS), is for CVE-2026-109618.

PHP2Shell

Below is the rule definition from the bulletin.

{
  "Name": "PHP2Shell",
  "Priority": 0,
  "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"
          }
        }
      ]
    }
  },
  "Action": { "Block": {} },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "PHP2Shell"
  }
}

This blocks requests that satisfy all three of the following conditions.

  • Method is POST
  • A cookie key exactly matches PHPSESSID, or the body contains PHPSESSID
  • The body contains PHP_SESSION_UPLOAD_PROGRESS

The following can be inferred from the definition.

  • Strings are compared case-sensitively (TextTransformations is NONE)
  • There is no condition on Content-Type
  • The body's OversizeHandling is CONTINUE. The determination is made based only on the range WAF can inspect (the first 8 KB for ALB)

Block-Oversize-Body

Block-Oversize-Body is a rule that blocks requests where the body exceeds 8,192 bytes (8 KB), and it only looks at body size. It is listed as a custom rule in the AWS Security Blog.

The AWS Security Bulletin introduces SizeRestrictions_BODY from the CRS, which performs the same function.
The custom rule (Block-Oversize-Body) is superior in terms of WCU savings (700 for CRS), customizability, and ease of log tracking.

The AWS Security Blog also states that for those already using CRS (AWSManagedRulesCommonRuleSet), SizeRestrictions_BODY performs the same function.

This custom rule is included in the verification template. The name in the Blog is Block-Oversize-Body, and the name in the template is block-oversize-body. The following JSON is the template rule with the 403 custom response (the configuration that returns the rule name) removed. The name and priority are the same values as in the template. For the metric name, the template prepends the WebACL name.

{
  "Name": "block-oversize-body",
  "Priority": 5,
  "Statement": {
    "SizeConstraintStatement": {
      "FieldToMatch": { "Body": { "OversizeHandling": "MATCH" } },
      "ComparisonOperator": "GT",
      "Size": 8192,
      "TextTransformations": [{ "Priority": 0, "Type": "NONE" }]
    }
  },
  "Action": { "Block": {} },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "block-oversize-body"
  }
}

Unlike PHP2Shell, the body's OversizeHandling is MATCH. The AWS Security Blog explains this is specified to also target bodies that exceed the inspection limit.

In the previous version of the template (commit cd6bafd), SizeRestrictions_BODY from the CRS was placed at priority 5. Even after switching to block-oversize-body in the current version, the matches for the same shaped requests and the position where the rule blocking at the 8 KB boundary switches were the same as in the previous version.

WAF log recording was split into two locations in the previous version.

  • Previous version (CRS SizeRestrictions_BODY)
    • The rule name aws-crs-size-restrictions-body attached to the WebACL appeared in nonTerminatingMatchingRules (Count configuration) or terminatingRuleId (Block configuration)
    • The rule ID SizeRestrictions_BODY within the managed rule group appeared in ruleGroupList. In Count configuration, even though the request passed with a 200, the terminatingRule action in ruleGroupList was recorded as BLOCK
  • block-oversize-body
    • The rule name appears in a single location: nonTerminatingMatchingRules (Count configuration) or terminatingRuleId (Block configuration). The action matches the request outcome: COUNT for Count configuration and BLOCK for Block configuration. For Block, responseCodeSent also records 403

Verification Environment

This verification environment has been made into a CloudFormation template and can be reproduced from the GitHub repository (commit febe2c7).

https://github.com/cm-suzuki-ryo/aws-waf-php2shell-post-guard

The template creates a new WebACL containing the two rules above and associates it with the ALB specified by LoadBalancerArn. The main parameters mentioned in this article are as follows.

  • BlockPhpUploadProgress=true (default) includes PHP2Shell. The name is block-php-upload-progress, priority is 4. The action follows RuleAction, returning 403 with the rule name on Block. Otherwise it is identical to the AWS Security Bulletin definition
  • BlockLargeBody=true (default is false) includes block-oversize-body
  • Setting RuleAction=Count makes all rules in the WebACL Count. Count records matches in WAF logs and metrics while allowing the request through. Use this to assess impact before switching to Block
Deployment command (example with RuleAction=Count)

This is the README deployment example with one line added for BlockLargeBody=true. It assumes a test ALB. If the ALB already has a WebACL attached, the association will be replaced by this WebACL. AllowedCountries=JP is the setting for a country-based rule that blocks POST requests from outside Japan. The template also includes a rate limiting rule. Please verify the impact of these rules before switching RuleAction to Block.

aws cloudformation deploy \
  --stack-name waf-post-guard \
  --template-file template.yaml \
  --region ap-northeast-1 \
  --parameter-overrides \
      AllowedCountries=JP \
      RuleAction=Count \
      BlockLargeBody=true \
      LoadBalancerArn=arn:aws:elasticloadbalancing:REGION:ACCOUNT_ID:loadbalancer/app/NAME/ID

The ALB is in the Tokyo region. Since a self-signed certificate is used for the HTTPS listener, -k is appended to curl. Unless otherwise noted, the results below use the configuration RuleAction=Block, BlockPhpUploadProgress=true, BlockLargeBody=true (the deployment example uses RuleAction=Count). Which rule blocked the request was confirmed using the x-waf-blocked-by header in the 403 response (which contains the rule name) and the WAF logs. Each request was sent once.

Verifying PHP2Shell Conditions with curl

I sent a POST satisfying all three PHP2Shell conditions and POSTs with each condition individually removed via curl, and observed the response codes.

# Cookie PHPSESSID + body PHP_SESSION_UPLOAD_PROGRESS
curl -sk -o /dev/null -w '%{http_code}\n' -X POST "https://<ALB DNS name>/" \
  -b "PHPSESSID=dummy" \
  -F "PHP_SESSION_UPLOAD_PROGRESS=dummy" \
  -F "a=b"

# Both as body fields
curl -sk -o /dev/null -w '%{http_code}\n' -X POST "https://<ALB DNS name>/" \
  -F "PHPSESSID=dummy" \
  -F "PHP_SESSION_UPLOAD_PROGRESS=dummy" \
  -F "a=b"
Request Result
POST, Cookie PHPSESSID, body contains PHP_SESSION_UPLOAD_PROGRESS 403
POST, body contains both PHPSESSID and PHP_SESSION_UPLOAD_PROGRESS 403
POST, multiple cookies (a=1; PHPSESSID=x), body contains PHP_SESSION_UPLOAD_PROGRESS 403
POST, body contains only PHP_SESSION_UPLOAD_PROGRESS 200
POST, only Cookie PHPSESSID 200
PUT, body contains PHPSESSID and PHP_SESSION_UPLOAD_PROGRESS 200

A POST with a manually constructed multipart body using --data-binary instead of -F also resulted in 403, the same as the first command.

PHP2Shell Inspects Only the First 8 KB of the Body

Allowed requests, including uninspected portions, are passed through in their entirety to behind the ALB.

https://docs.aws.amazon.com/waf/latest/developerguide/waf-oversize-request-components.html

In a configuration without block-oversize-body (BlockLargeBody=false, RuleAction=Block), I sent multipart POST requests. The body was structured with a large pad field followed by PHPSESSID and PHP_SESSION_UPLOAD_PROGRESS (both with value dummy), varying only the position of PHP_SESSION_UPLOAD_PROGRESS. The values in the table represent the end position of PHP_SESSION_UPLOAD_PROGRESS, with the beginning of the body as position 0.

End position of PHP_SESSION_UPLOAD_PROGRESS (bytes) Result with PHP2Shell only
8,183 403
8,191 403
8,193 200
8,237 200

POSTs where PHP_SESSION_UPLOAD_PROGRESS fits within the first 8,192 bytes resulted in 403; POSTs where it did not fit resulted in 200.

When also setting block-oversize-body to Block (BlockLargeBody=true) and sending the same shaped POSTs with varying total body sizes, the blocking rule differed based on the position of the two strings.

Total body size (bytes) Position of the two strings Blocking rule
8,106, 8,196, 8,206 Within the first 8,192 bytes block-php-upload-progress (PHP2Shell)
8,256–9,206 PHP_SESSION_UPLOAD_PROGRESS end position is beyond 8,192 bytes block-oversize-body

The 8,196-byte and 8,206-byte POSTs also satisfy the block-oversize-body condition (exceeds 8,192 bytes). However, block-php-upload-progress at priority 4 is evaluated before block-oversize-body at priority 5 and blocks them first.

The publicly available PoC is of the latter form. Based on reading the source (not executed), the multipart body is structured with more than 1,000 dummy fields followed by PHPSESSID and PHP_SESSION_UPLOAD_PROGRESS. A file and a huge field follow after that. When I sent a 1,056,388-byte POST reproducing this order with harmless dummy values (the two strings located approximately 56 KB from the start of the body), it was blocked with 403 by block-oversize-body.

Conditions Under Which Non-Attack Requests Are Blocked

Legitimate Requests Exceeding 8 KB Are Also Blocked by block-oversize-body

In Count configuration (RuleAction=Count), I sent the following five types of requests and all responses were 200. The WAF logs recorded all five as matching block-oversize-body (nonTerminatingMatchingRules).

Request Body size
JSON POST 20,013 bytes
Image upload (multipart POST) 102,710 bytes
PUT 15,000 bytes
PATCH 15,000 bytes
GET with JSON body 20,013 bytes

With RuleAction=Block, these were all blocked with 403.

I also verified body sizes around the threshold (8,192 bytes) and sizes far exceeding the WAF inspection range (first 8,192 bytes) with RuleAction=Block. These are the results of sending harmless urlencoded POSTs with only the body size varied.

Body size (bytes) Result
8,191 200
8,192 200
8,193 403
1,048,576 403
5,242,880 403

For POSTs of 8,193 bytes or more, REQUEST_BODY was recorded in oversizeFields of the WAF log. The inspected range (requestBodySizeInspectedByWAF) was 8,192 bytes.

PHP2Shell Also Blocks JSON and text/plain Bodies

The following two POSTs both resulted in 403.

  • Content-Type application/json, body {"PHPSESSID":"dummy","PHP_SESSION_UPLOAD_PROGRESS":"dummy"}
  • Content-Type text/plain, body PHPSESSID=dummy PHP_SESSION_UPLOAD_PROGRESS=dummy

For example, if a form for posting PHP articles has a body containing both of these strings, that POST will also be blocked.

I also tested a rule that adds an AND condition of "Content-Type header contains multipart/form-data" to the AWS Security Bulletin rule. This rule compares Content-Type in lowercase. When added to the WebACL in Count mode and the multipart POST and the above two POSTs were sent, this rule matched the multipart POST but did not match the JSON or text/plain POSTs. It also did not match the POST sent without specifying Content-Type using --data-binary (where curl adds application/x-www-form-urlencoded). This rule has a narrower blocking scope than the bulletin's definition. A case-sensitive rule (not converting to lowercase) did not match a POST with Content-Type Multipart/Form-Data.

When block-multipart-post in the Template Is Enabled

The template also includes a rule called block-multipart-post (enabled with BlockMultipartPost=true; disabled by default). This is a rule that blocks any POST containing multipart/form-data in Content-Type, regardless of size or content. It is based on Block-Multipart-FormData from the AWS Security Blog with a POST condition added. Legitimate file uploads and multipart form submissions will also be blocked.

I examined how block-php-upload-progress (PHP2Shell) and block-oversize-body overlap when this rule is enabled. With BlockMultipartPost=true and RuleAction=Count, I sent three types of multipart POSTs and tabulated the matches recorded in the WAF logs (— indicates no match).

Request block-multipart-post block-php-upload-progress (PHP2Shell) block-oversize-body
Multipart POST, Cookie PHPSESSID, body contains PHP_SESSION_UPLOAD_PROGRESS Match Match —
Normal multipart POST Match — —
Multipart POST reproducing the PoC order (1,056,388 bytes) Match — Match

block-multipart-post is evaluated before block-php-upload-progress and block-oversize-body. When enabled with RuleAction=Block, multipart POSTs are stopped here and evaluation does not proceed to the subsequent rules. The multiple matches in the table occurred because evaluation continued under Count. When disabled, normal multipart POSTs without the two strings pass through if the body is 8,192 bytes or less. Those exceeding 8 KB (such as a 102,710-byte image upload) are blocked by block-oversize-body.

Summary

AWS Security Bulletin 2026-143-AWS and the AWS Security Blog introduce AWS WAF rules as an interim mitigation for two PHP vulnerabilities. Of these, PHP2Shell and Block-Oversize-Body were verified on an ALB.

If you are considering adopting these two rules as AWS WAF mitigations, we hope the evaluation results presented here will be useful as a reference.

However, please note that WAF rules are an interim mitigation, and the fundamental solution is to update PHP to a fixed version (8.5.12, 8.4.27, 8.3.36, or 8.2.35).

The following AWS Security Blog article summarizes mitigations using AWS WAF rules and PHP-side configurations. We recommend referring to it as well.

https://aws.amazon.com/jp/blogs/security/using-aws-waf-to-apply-mitigations-for-php2shell-rce-vulnerabilities-cve-2026-109618-and-cve-2026-109619/

The background on how Amazon Threat Intelligence identified exploitation of the vulnerability prior to public disclosure, and the recommended response, can be found in the following article.

https://aws.amazon.com/jp/blogs/security/amazon-threat-intelligence-identifies-php2shell-exploitation-prior-to-public-disclosure-in-madpot/

Share this article

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