luizmachado.dev

PT EN

PHP2Shell on AWS: simulate, detect, and fix with AWS services

Quick background on the flaw (so this post stands on its own). PHP2Shell is the disclosure nickname for two critical, unauthenticated RCE vulnerabilities in the PHP interpreter, CVE-2026-109618 and CVE-2026-109619. They are memory safety bugs in the C engine, not application bugs: 109618 is memory corruption triggered when an out-of-memory (OOM) condition interrupts PHP mid-initialization and the cleanup operates over partial state; 109619 is stale state / use-after-free exposed through worker reuse (a later request served by the same long-lived worker uses state freed before the restart). The consequences that drive everything below: there is no input to "sanitize," so the only complete fix is to update the interpreter binary and restart the workers; edge controls (WAF, request-shape limits) only narrow the window; and reliable detection needs to look at the host (anomalous crashes/OOM, process tree, outbound network), not URL signatures.

Goal

Operationalize on AWS the three defensive jobs against PHP2Shell:

  1. Discover exposure across the fleet (which hosts/images/functions run affected PHP).
  2. Reduce the window at the edge while the patch hasn't landed (WAF/CloudFront/ALB).
  3. Fix at scale per compute type (EC2, containers, Beanstalk, Lambda, Lightsail) and detect use of the window (GuardDuty/CloudWatch/Security Hub).

All without an exploit, strictly defensive: discover, contain, fix, detect.

AWS official position (bulletin 2026-143-AWS)

AWS states: "AWS services are not impacted by these issues", but customers who run affected PHP versions inside services may be impacted. There was no established cross-customer propagation as of the date.
Source: AWS Security Bulletin 2026-143-AWS.

Per-service guidance (from the bulletin):

╔════════════════════╦══════════════════════════════════════════════════════════╗
║ Service            ║ Status / action                                            ║
╠════════════════════╬══════════════════════════════════════════════════════════╣
║ Amazon WorkMail    ║ AWS applied controls and moved to PHP 8.3.36. No action.   ║
║ Elastic Beanstalk  ║ New platform versions for PHP 8.2–8.5. Managed Platform    ║
║                    ║ Updates: arrives in the next window. Otherwise: update by  ║
║                    ║ hand.                                                      ║
║ AWS Lambda         ║ There is NO managed PHP runtime. PHP via custom runtime /  ║
║                    ║ container: update PHP and redeploy.                        ║
║ Amazon Linux       ║ PHP packages updated in commercial regions.                ║
║ Amazon Lightsail   ║ Blueprints updated. Does NOT update a running instance in  ║
║                    ║ place → create a new instance and migrate.                 ║
╚════════════════════╩══════════════════════════════════════════════════════════╝

Source: AWS Security Bulletin 2026-143-AWS.

Affected and fixed versions (ranges confirmed by the bulletin):

PHP 8.5: affected 8.5.0–8.5.11  → fixed 8.5.12
PHP 8.4: affected 8.4.0–8.4.26  → fixed 8.4.27
PHP 8.3: affected 8.3.0–8.3.35  → fixed 8.3.36
PHP 8.2: affected 8.2.0–8.2.34  → fixed 8.2.35
PHP 5.4–8.1: NO upstream fix (EOL) → "Migration to a supported branch is required."

Source: AWS Security Bulletin 2026-143-AWS.

AWS WAF: the AWS WAF PHP Managed Rule Group is being updated for both CVEs. Customers on the default version are protected automatically; those who pinned a version need to update to receive the protection. Follow the AWS WAF Managed Rules changelog and subscribe to the Managed Rules SNS topic. The bulletin reinforces: patching is the only complete fix; WAF is a compensating control.
Source: AWS Security Bulletin 2026-143-AWS.

Map: the 3 jobs and the AWS services

                    ┌──────────────────────── EDGE ─────────────────────────┐
  Internet ──▶ CloudFront ──▶ AWS WAF ──▶ ALB ──▶  PHP on EC2 / ECS / EKS
                               │  (PHP Managed Rule Group + Size + Rate)      │
                               └── narrows the window (does NOT fix) ─────────┘
                                                      │
   DISCOVER             REDUCE WINDOW                 │ logs            DETECT USE
   ────────             ─────────────                 ▼                 ──────────
   Inspector (EC2/ECR)  AWS WAF (managed+custom)   CloudWatch Logs   GuardDuty Runtime
   SSM Inventory        Shield / CloudFront        + Logs Insights   (process/network)
        │                                          (crash/OOM)             │
        └──────────────┐                                   └───────┐      │
                       ▼                                           ▼      ▼
   FIX AT SCALE: SSM Patch Manager (EC2) · Image Builder/CI (containers) ·
   Beanstalk platform update · Lambda redeploy · Lightsail migrate
                       │                                           │
                       └────────▶ Security Hub (aggregates findings) ◀──┘
                                        │
                                        ▼  EventBridge → SSM Automation / SNS (response)

1. Discover what is vulnerable (Amazon Inspector + SSM Inventory)

Amazon Inspector scans EC2, images in ECR, and Lambda functions, cross-referencing packages with 50+ feeds (NVD, MITRE, vendor advisories), updated at least once a day. PHP is a supported language for EC2 (agentless/VM Scanner) and for ECR.
Source: Amazon Inspector — Supported OS and languages.

Two details that change your strategy:
- OS package detections on Linux cover only the default repository (rpm/dpkg), they do not include third-party applications, extended-support repositories, or application streams, except where documented. In other words: PHP installed via a third-party repository (Remi, PPA ondrej/sury) may not be flagged as an OS package.
- Lambda has no managed PHP runtime; Inspector does not list PHP among the Lambda runtimes. PHP on Lambda only exists via custom runtime/container → what scans it is Inspector on the image in ECR, not the Lambda runtime scan.
Source: Amazon Inspector — Supported OS and languages ; AWS Security Bulletin 2026-143-AWS.

Practical — filter findings by the CVEs:

# Inspector findings for both CVEs across the whole account/region
aws inspector2 list-findings \
  --filter-criteria '{"vulnerabilityId":[
      {"comparison":"EQUALS","value":"CVE-2026-109618"},
      {"comparison":"EQUALS","value":"CVE-2026-109619"}]}' \
  --query 'findings[].{res:resources[0].id,sev:severity,pkg:packageVulnerabilityDetails.vulnerablePackages[0].name}' \
  --output table

Practical — inventory of the PHP version across the fleet (SSM Inventory / Run Command):

# Collect the PHP version served/installed on SSM-managed instances
aws ssm send-command \
  --document-name "AWS-RunShellScript" \
  --targets '[{"Key":"tag:app","Values":["web-php"]}]' \
  --parameters 'commands=["php -v | head -1 || true","(php-fpm -v 2>/dev/null | head -1) || true"]' \
  --comment "PHP version inventory - php2shell"

Tool recommendation — Amazon Inspector
(a) Does well: continuous multi-target scan (EC2/ECR/Lambda) with automatic CVE correlation and native integration with Security Hub; already recognizes PHP.
(b) Limits: OS only the default repo → gap for PHP from a third-party repository; requires the SSM agent/role for agent-based EC2; it does not "fix," only finds; it does not see the version actually served by each pool (the CLI php -v may differ from the FPM pool / Apache module serving a site, check per pool, see §3.1).
(c) Open-source alternative: Trivy or Grype in the build pipeline and against images/host (covers third-party packages better in many cases); syft for SBOM.
(d) For this context: on an existing AWS fleet, Inspector is the lowest-friction option for broad coverage; add Trivy in CI to close the third-party PHP gap in images.

2. Reduce the window at the edge (AWS WAF + CloudFront/ALB)

Lowest-effort path: ensure the AWS WAF PHP Managed Rule Group is on the default version (updated for both CVEs); if you pinned a version, update it; subscribe to the Managed Rules SNS.
Source: AWS Security Bulletin 2026-143-AWS.

As a complement, custom request-shape rules (the PHP interim guidance maps to limits you can enforce at the edge: decoded request size, field count, number of multipart parts, headers per part, and nesting depth): body size limit, rate-based, and optionally block/throttle of multipart/form-data. A public study with ALB implemented exactly this via CloudFormation (blocking multipart POST with an AndStatement of method+content-type, rate-based per IP and per JA4, country rule).
Source: Classmethod — AWS WAF rules on ALB after the php2shell advisory.

Honest limitations of this WAF (from the same study): the multipart rule also blocks legitimate uploads (apps with a file form can't use it); rate-based rules let requests through before blocking (in the tests, 23 to 52 POSTs got through); the author did not verify effective protection against php2shell and reinforces that the fundamental fix is to update PHP.
Source: Classmethod.

Practical — body size limit rule (narrows the 109618/OOM window):

{
  "Name": "limit-body-size",
  "Priority": 10,
  "Action": { "Block": {} },
  "Statement": {
    "SizeConstraintStatement": {
      "FieldToMatch": { "Body": { "OversizeHandling": "MATCH" } },
      "ComparisonOperator": "GT",
      "Size": 1048576,
      "TextTransformations": [{ "Priority": 0, "Type": "NONE" }]
    }
  },
  "VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "limitBodySize" }
}

Note: by default AWS WAF inspects only the first ~8 KB of the body (configurable depending on the association type); that's why OversizeHandling matters and why this is window reduction, not shielding.

Tool recommendation — AWS WAF Managed Rule Group
(a) Does well: updated and operated by AWS, enables protection for known vectors with low effort; integrates with CloudFront/ALB/API GW and logging.
(b) Limits: based on known signatures/vectors, and an unauthenticated memory-corruption flaw may have new variations; custom multipart/size rules can block legitimate traffic; rate-based leaks requests. Does not fix.
(c) Open-source alternative: ModSecurity + OWASP CRS (on Nginx/Apache) with the request-shape limits above (e.g., client_max_body_size, header/buffer caps, rejection of unexpected nesting).
(d) For this context: if you're already on CloudFront/ALB, enable the managed group + body size limit as a way to buy time, with WAF logging to CloudWatch; treat it as a bridge until the patch.

3. Fix at scale, per compute type

The fix changes depending on where PHP runs. The rule that holds for all: update the binary and restart what serves (long-lived workers keep the old binary in memory, exactly the vector for 109619).

3.1 EC2 — Systems Manager Patch Manager + Run Command

Updated PHP packages are available for Amazon Linux in the commercial regions.
Source: AWS Security Bulletin 2026-143-AWS.

Typical flow with Patch Manager: patch baseline → patch groups by tag → maintenance window (or on-demand) with a Scan operation and then Install. You guarantee the worker restart with Run Command right after.

Practical — on-demand patch + worker restart:

# 1) install updates (includes PHP from the default repo) via Patch Manager
aws ssm send-command \
  --document-name "AWS-RunPatchBaseline" \
  --parameters 'Operation=Install' \
  --targets '[{"Key":"tag:app","Values":["web-php"]}]'

# 2) restart the workers so the new binary comes into use
aws ssm send-command \
  --document-name "AWS-RunShellScript" \
  --targets '[{"Key":"tag:app","Values":["web-php"]}]' \
  --parameters 'commands=[
     "systemctl restart php*-fpm 2>/dev/null || systemctl restart php-fpm 2>/dev/null || true",
     "systemctl restart apache2 2>/dev/null || systemctl restart httpd 2>/dev/null || true",
     "php -v | head -1"]'

Watch out for the repository gap: Inspector and Patch Manager rely on the default repository (rpm/dpkg). PHP installed via Remi/ondrej/sury may not be updated by AWS-RunPatchBaseline nor flagged by Inspector as an OS package; on those hosts, update from the same repository the PHP came from.
Source: Amazon Inspector — Supported OS and languages.

3.2 Containers (ECS/EKS) — rebuild the image, don't "patch" the container

Containers are not fixed while running. The flow is: update PHP in the base image → rebuild → push to ECR → Inspector scans the image → redeploy forcing a new task/pod. Running tasks/pods remain vulnerable until the redeploy.

Practical — force a new deployment on ECS after a fixed image:

aws ecs update-service --cluster prod --service web-php --force-new-deployment

On EKS: kubectl rollout restart deployment/web-php after the new fixed image tag is referenced. Make sure imagePullPolicy pulls the new tag (avoid an ambiguous :latest, pin the digest).

3.3 Elastic Beanstalk — update the platform version

AWS released new platform versions for PHP 8.2–8.5. With Managed Platform Updates enabled, it arrives in the next maintenance window; otherwise, update the environment's platform version manually.
Source: AWS Security Bulletin 2026-143-AWS.

3.4 Lambda — rebuild + redeploy (there is no managed PHP runtime)

There is no managed PHP runtime. If you run PHP via a custom runtime (e.g., Bref/layer) or a container image, update PHP and redeploy.
Source: AWS Security Bulletin 2026-143-AWS.

3.5 Lightsail — create a new instance and migrate

Blueprints updated in all regions, but AWS does not update running instances in place → create a new instance from the updated blueprint and migrate.
Source: AWS Security Bulletin 2026-143-AWS.

Tool recommendation — SSM Patch Manager
(a) Does well: patching at scale by tag, scheduled in maintenance windows, with a compliance report; integrates with Run Command for the restart.
(b) Limits: depends on the distro advisory timing and on the default repo (third-party PHP gap); it does not cover containers or Lambda; it does not restart workers on its own.
(c) Alternative: EC2 Image Builder to generate an already-fixed golden AMI (immutable replacement), Ansible/config mgmt for hosts with a third-party repository, and CI rebuild for container images.
(d) For this context: mutable EC2 → Patch Manager + Run Command; immutable fleet/ASG → Image Builder + AMI rotation; containers → CI rebuild + ECR + redeploy.

4. Detect use of the window (GuardDuty + CloudWatch Logs + Security Hub)

GuardDuty Runtime Monitoring (EC2, ECS/Fargate, EKS) observes process execution and network activity on the host/container and generates findings for suspicious runtime behavior, being the managed equivalent of a host rule that alerts when a web worker (php-fpm, apache2/httpd, lsphp, frankenphp) spawns a shell or opens an outbound connection. GuardDuty uses AWS threat intelligence; it was MadPot (AWS's honeypot) that recorded the exploitation attempt on 2026-10-17, four days before the public disclosure.

The exact finding type names of Runtime Monitoring (e.g., suspicious execution, reverse shell, mining) change and should be confirmed in the GuardDuty documentation before you write automations that depend on the finding name. Don't cite the name from memory, validate it in the console/docs.

Practical — detect a burst of SIGSEGV/OOM in CloudWatch Logs Insights (ship the php-fpm/Apache logs via the CloudWatch agent):

fields @timestamp, @message
| filter @message like /SIGSEGV|SIGABRT|Segmentation fault|Allowed memory size .* exhausted/
| stats count() as eventos by bin(5m)
| sort eventos desc

Alert on an anomalous pattern (clustering over 5 min), not an isolated event; baseline first, since many crashes/OOMs are legitimate (traffic spikes, heavy libs). Since the logs are already in CloudWatch, you also meet the requirement of forwarding logs off the host (an attacker with permissions tampers with the local log).

Aggregate and respond: send findings from Inspector + GuardDuty to Security Hub; trigger a response with EventBridge → SSM Automation (e.g., isolate the instance by swapping the security group, snapshot the volume for forensics, notify via SNS). This materializes the "assume the window was open" posture: since there was exploitation before disclosure, after the patch look for new/changed .php files in web roots and upload directories, unexpected cron jobs, and outbound connections from the web uid, now with central telemetry.

Tool recommendation — GuardDuty Runtime Monitoring
(a) Does well: managed process/network visibility, with AWS threat intel (includes MadPot), without you maintaining rules; integrates with Security Hub/EventBridge.
(b) Limits: Runtime Monitoring requires an agent (coverage varies by platform); findings are about behavior, not php2shell-specific; cost per volume.
(c) Open-source alternative: Falco/Tracee/auditd (an auditd execve rule scoped to the web user, plus Falco for the process tree) forwarding to a SIEM (OpenSearch), with the process-tree rules written by you.
(d) For this context: AWS-native and a small team → GuardDuty Runtime Monitoring; if you already run Falco in the cluster, use it as a second signal and reserve GuardDuty for the account/network layer.

Practical example — end-to-end AWS runbook (in order)

Run it in an isolated lab account/VPC before running it in production (no IGW, no real data, no real credentials, isolate for real).

1. DISCOVER    aws inspector2 list-findings (filter CVE-2026-109618/619)
               + SSM Run Command inventorying php -v / php-fpm -v by tag
               → list of affected hosts/images/environments, by compute type

2. CONTAIN     WAF: PHP Managed Rule Group on the default version (or update the pin)
   (edge)      + limit-body-size rule (1 MB) + rate-based; WAF logging → CloudWatch
               → narrows the window; does NOT close it

3. DETECT      GuardDuty Runtime Monitoring enabled on the targets
               + Logs Insights alerting on anomalous SIGSEGV/OOM
               → you start to see attempts/crashes

4. FIX         EC2: Patch Manager Install + Run Command restart workers
               ECS/EKS: rebuild image → ECR → Inspector → force-new-deployment
               Beanstalk: platform update   Lambda: redeploy   Lightsail: migrate
               → closes the flaw (new binary + restart)

5. VERIFY      Inspector re-scan (finding disappears) + endpoint diag per pool
               + post-compromise sweep → rotate secrets if there's an IOC

Common pitfalls (AWS-specific)

  1. "Inspector didn't flag it, so I'm clean." The OS scan covers only the default repo; PHP from Remi/ondrej/sury and application streams can slip through, and containers/Lambda are covered by different paths (image in ECR, not Lambda runtime). Avoid: cross Inspector with a per-pool version inventory (Run Command) and with Trivy in CI.

  2. "I updated the AMI / the base image." But the running ECS tasks/pods and the long-lived FPM workers still run the old binary. Recognize: php -v on the new host says fixed, but the served endpoint still crashes/serves the old version. Avoid: force-new-deployment/rollout restart and worker restart (109619 lives precisely in worker reuse).

  3. Treating the WAF Managed Rule Group as the fix. It's a compensating control: the signature covers a known vector, the multipart rule breaks legitimate uploads, and the rate-based one leaks requests. AWS itself says that patching is the only complete fix. Avoid: WAF = bridge; patch + restart = done.

  4. Assuming in-place update where none exists. Lightsail does not update a running instance (you must migrate); Beanstalk needs a new platform version; Lambda needs a redeploy. Avoid: treat each compute by its own path (section 3).

Resources to go deeper

AWS primary sources:
- AWS Security Bulletin 2026-143-AWS: per-service guidance, affected/fixed versions, WAF Managed Rule Group. The canonical AWS reference for this CVE; it declares itself evolving, so keep coming back to it.
- Amazon Inspector — Supported OS and programming languages: confirms PHP supported on EC2/ECR, the absence of a PHP runtime on Lambda, and the "default repo" limit.
- AWS WAF Managed Rules changelog + the Managed Rules SNS topic (cited in the bulletin): to know when the PHP rule's protection landed and to version it.

Practical / high authority (with declared limitations):
- Classmethod — WAF rules on ALB after php2shell: WAF CloudFormation (multipart/rate/country) with measurements and honest limitations; the author does not prove protection and points to patching as the fundamental fix.