AWS Builder Center
Find It, Patch It, Watch It: A Community Checklist for php2shell on AWS

Find It, Patch It, Watch It: A Community Checklist for php2shell on AWS

php2shell is the nickname for two critical flaws in the PHP runtime itself, CVE-2026-109618 and CVE-2026-109619, both scored CVSS v4.0 9.2. They allow an unauthenticated attacker to run code on a PHP web server, and exploitation in the wild was reported on October 21, 2026, the same day PHP shipped fixes.

Series: AI Security (3 articles)

  1. 3
    Find It, Patch It, Watch It: A Community Checklist for php2shell on AWS This article
DevSecOps
 

What is php2shell?

php2shell is a pair of memory-safety bugs in the PHP engine, fixed on October 21, 2026. PHP rates both CVSS v4.0 9.2; the Amazon Linux Security Center  scores them CVSS v3 8.1 (Important).
CVEPHP's titleWhat goes wrongAdvisory
CVE-2026-109618RCE via OOM corruptionAn out-of-memory error interrupts PHP while request data is only partly initialized. Request cleanup then treats that data as valid.GHSA-w8hw-w9fx-j79w 
CVE-2026-109619RCE via stale global stateRequest-owned state survives after its memory is freed. The next request on the same worker can use it before it is reset.GHSA-6wmf-4rv5-p3rp 
Who is at risk. Per the PHP security advisory , every PHP installation serving web requests: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. PHP rates mod_php highest, with a demonstrated unauthenticated exploit that fires before any application code runs. Sansec  traces the vulnerable code back to PHP 5.4, released in 2012.
PHP branchFixed releaseNotes
8.58.5.12
8.48.4.27
8.38.3.36
8.28.2.35Branch reaches end of life on December 31, 2026
8.1, 8.0, 7.4No official releasePHP published minimal source backports; otherwise use an LTS vendor or upgrade
7.3 and olderNo fix from PHPUpgrade, or use a commercial LTS build
How it went public. The bugs leaked a day before the coordinated release, and attacks started before the patch landed.
 
 
When (UTC)What happened
Oct 21, ~14:00PHP ships fixed releases; Calif, Wiz, OpenAI and Quarkslab publish details (Cloudflare )
Oct 21, 09:18Aikido reports exploitation in the wild (Aikido )
Oct 21, 07:43Cloudflare reports early attacks (Sansec )
Oct 21, 03:02Cloudflare's WAF rules go live for all plans
Oct 20CVE IDs leak on Hacker News; PHP posts an advance notice
Aug 24Wiz Research reports the bugs to PHP
Aug 11Calif reports the first bug to PHP
One detail worth sharing with your team: Wiz researchers said  these flaws had existed for decades, and frontier AI models are what made a working exploit chain practical.

How it works

Both bugs are about what PHP does when a request goes wrong, not about anything in your code.
 
 
 
For CVE-2026-109618, PHP's advisory  lists three ways to push a request out of memory while it still looks within limits. A multipart body without a usable Content-Length can slip past post_max_size. A single part can carry unlimited headers that cost far more memory inside PHP than on the wire. Empty file fields with nested names multiply array work without counting toward max_file_uploads.
Why mod_php is worst: Apache runs PHP's request cleanup even when request startup failed, so the corrupted state is processed before any application code runs. PHP-FPM kills the child when startup fails, which is why PHP calls FPM susceptible but has no complete FPM exploit for this CVE.
CVE-2026-109619 is about reuse. A worker serves many requests, and some global pointers that should reset between them don't. The confirmed paths run through session upload progress, which is on by default, and fatal error backtraces on PHP 8.5.
 

Am I affected? Using the Aikido checker responsibly

The fastest answer is still the version number on the host; php2shell.aikido.io  is a useful outside-in second opinion, not a verdict. It is a free, owner-run check from Aikido that needs no signup and probes a public URL for the multipart memory-exhaustion behavior behind both CVEs.
 
 
ModeWhat it sendsRisk to your siteWhat a result means
Default checkFingerprint requests plus repeated timing requestsLow. The probe assumes a 128 MB memory_limit, so a lower limit can still fail a requestIdentifies PHP on that path; timing alone cannot tell patched from vulnerable PHP
Definitive check (opt-in)A larger payloadCan crash a PHP worker and interrupt live requestsA crash points to vulnerable PHP; a clean result can mean patched PHP or a memory limit above the probe's reach
A few rules before you run it:
  • Only test what you own or are authorized to test. Run the definitive check outside peak hours, and tell whoever is on call first.
  • Point it at a real PHP path, not a static page or a CDN-cached URL. Localhost and private networks are blocked.
  • Inconclusive is common. PHP 7.4 to 8.0, a WAF block, a timeout or missing PHP signals all leave the result undetermined. A blocked request is not proof of safety.
  • Mind what you submit. The service logs your IP and the tested URL (without its query string), so keep secrets out of the path.
Then confirm on the host itself. Remember that distributions backport fixes, so trust your vendor's advisory over the upstream version string:
1
2
3
4
php -v | head -n1
php -i | grep -E '^(memory_limit|post_max_size|session.upload_progress.enabled) '
# Amazon Linux 2023
rpm -qa 'php*' | sort

Find your PHP surface on AWS

You can't patch what you can't see, and PHP tends to hide in places nobody owns anymore: a marketing WordPress site on Lightsail, an old Beanstalk environment, a Lambda function someone built with a PHP layer. Start with a list.
Where PHP runsHow to find itWhat to check
EC2 and Auto Scaling groupsSystems Manager Inventory (AWS:Application) or Run CommandPHP package and version, plus the SAPI (mod_php vs PHP-FPM)
Elastic Beanstalkdescribe-environments, filtered on PHP platformsThe platform version and the PHP release it bundles
ECS, EKS, Fargate, App RunnerECR repositories, task definitions and pod specsThe PHP version inside the image, not just the tag
LambdaFunctions on provided.al2023 or provided.al2 with PHP layersThe layer ARN and version
LightsailInstances launched from WordPress or LAMP blueprintsphp -v over SSH
Anything hosted for youYour agency or managed hostAsk them for the PHP version and their patch date
If Amazon Inspector is enabled, ask it first. Findings depend on vendor advisory data, so an empty result today is not a clean bill of health, and PHP compiled from source inside a container image may not appear as a package finding at all.
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Inspector: any findings for either CVE?
aws inspector2 list-findings \
--filter-criteria '{"vulnerabilityId":[{"comparison":"EQUALS","value":"CVE-2026-109618"},{"comparison":"EQUALS","value":"CVE-2026-109619"}]}' \
--query 'findings[].[resources[0].type,resources[0].id,severity]' --output table

# Elastic Beanstalk: which environments run a PHP platform?
aws elasticbeanstalk describe-environments \
--query "Environments[?contains(SolutionStackName, 'PHP')].[EnvironmentName,SolutionStackName]" \
--output table

# EC2 via Systems Manager: installed PHP packages on one instance
aws ssm list-inventory-entries --instance-id i-0123456789abcdef0 \
--type-name AWS:Application --filters Key=Name,Values=php,Type=BeginWith

# EC2 fleet: ask every tagged instance directly
aws ssm send-command --document-name AWS-RunShellScript \
--targets Key=tag:Runtime,Values=php \
--parameters 'commands=["php -v | head -n1"]'

# Container images: check what is inside, not what the tag claims
docker run --rm --entrypoint php <account>.dkr.ecr.<region>.amazonaws.com/<repo>:<tag> -v
Adjust the tag filter to however you label workloads. If you run AWS Organizations, repeat the Inspector query from the delegated administrator account to cover every member account at once.

Fix it on AWS

Patching is the only complete fix, and on AWS the path depends on who owns the PHP binary. As of October 21, 2026, several AWS-managed paths were still waiting on packages, so check the linked status pages before you plan around them.
 
Where PHP runsPath to the fixStatus on Oct 21, 2026
Amazon Linux 2023Upgrade the php8.x packages once the ALAS advisory publishes, then restart PHPphp8.2 to php8.5: Pending Fix; php8.1: No Fix Planned (ALAS )
Amazon Linux 2The core php package gets no fix. Move to AL2023 or a supported PHP buildCore php: No Fix Planned; php8.2 extra: Pending Fix
Elastic BeanstalkMove each environment to the newest platform version instead of waiting for the maintenance windowAWS says the latest platform version carries the mitigation  and running environments update through Managed Platform Updates. The Oct 20 platform release  still shipped 8.5.11 and 8.4.26
ECS, EKS, Fargate, App RunnerRebuild on a base image with fixed PHP, push to ECR, roll the serviceAikido publishes free patched images  for 8.2 to 8.5 on its public ECR
Lambda with a PHP layer (for example, Bref)Move to a layer version built on a fixed PHP release and redeployDepends on your layer maintainer
Lightsail WordPress or LAMP (Bitnami)No in-place PHP upgrade; launch a fresh blueprint and migrate, per the AWS Knowledge Center Snapshot first; apply the interim settings below until then
PHP 8.1 or older, anywhereUpgrade to 8.4 or 8.5, use a commercial LTS build, or build from PHP's source backportsNo official PHP release
 
The commands, once fixed builds are available to you:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Amazon Linux 2023: pull the repo version that carries the fix, then restart
sudo dnf check-release-update
sudo dnf upgrade --releasever=latest 'php8.4*'
sudo systemctl restart php-fpm httpd

# Elastic Beanstalk: find the newest platform version for your branch...
aws elasticbeanstalk list-platform-versions \
--filters '[{"Type":"PlatformBranchName","Operator":"=","Values":["PHP 8.4 running on 64bit Amazon Linux 2023"]}]' \
--query 'PlatformSummaryList[0].PlatformArn'
# ...and apply it now
aws elasticbeanstalk update-environment --environment-name my-env --platform-arn <arn-from-above>

# ECS: rebuild the image (FROM php:8.4.27-fpm or newer), push, then roll tasks
aws ecs update-service --cluster prod --service web --force-new-deployment
Two things to plan for after the upgrade:
  • Restart every worker. A package upgrade does nothing for PHP-FPM children or Apache processes already in memory.
  • Expect a new memory behavior. Per PHP's advisory , patched PHP replaces the old out-of-memory jump with a soft limit plus a hard process exit at a higher threshold. A worker that blows its memory budget now dies immediately, so watch for it in load tests.
Then assume you may already have been hit. Aikido recommends  checking for compromise back to at least June 2026: web shells, unexpected or modified PHP files, new admin users and unusual outbound connections. The next two sections cover how to do that on AWS.

Reduce exposure while you patch

None of this makes unpatched PHP safe. PHP says these measures only narrow specific attack paths, and Aikido warned early on that configuration changes alone did not stop the exploits. Use them to buy hours, not weeks.

PHP settings from the official advisory

SettingTargetsWhere to set itTrade-off
memory_limit = -1CVE-2026-109618php_admin_value in the FPM pool or Apache config, so apps can't override itAn underlying allocation failure can still reach the bug; pair it with a container or cgroup memory limit, never RLIMIT_AS or RLIMIT_DATA
post_max_size = 8M (no uploads) or 32M (uploads), with upload_max_filesize below itCVE-2026-109618php.ini or FPM poolLarge forms and uploads fail
Lower max_input_nesting_leveland bound input variablesCVE-2026-109618php.ini or FPM poolDeeply nested forms may break
session.upload_progress.enabled = 0CVE-2026-109619php.ini or FPM pool, then restart every worker. Not ini_set() or .user.iniPHP-native upload progress bars stop
fatal_error_backtraces = 0 (PHP 8.5 only)CVE-2026-109619php.ini or FPM poolLess detail in fatal error logs
On Amazon Linux 2023 with PHP-FPM, that looks like this. On Elastic Beanstalk, ship the same values in an .ini file under /etc/php.d/ through .ebextensions or a .platform hook.
1
2
3
4
5
6
7
; /etc/php-fpm.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 6M
php_admin_flag[session.upload_progress.enabled] = off
; PHP 8.5 only
php_admin_flag[fatal_error_backtraces] = off

At the edge: AWS WAF, ALB and CloudFront

No complete WAF rule exists for php2shell, and I hadn't found an AWS-managed rule group update for it at the time of writing. Check the AWS WAF managed rules changelog, and meanwhile add your own rules in Count mode, read the logs, then switch to Block.
  • Flag the upload-progress field. The confirmed CVE-2026-109619 path sends the PHP_SESSION_UPLOAD_PROGRESSfield in a multipart body of roughly 450 bytes, well inside what AWS WAF inspects.
  • Block multipart where you don't need it. PHP's advisory says to block multipart requests at the frontend if your app doesn't use them. Match the header case-insensitively, because PHP also accepts MULTIPART/FORM-DATA.
  • Know the body inspection limit. AWS WAF on an ALB  sees only the first 8 KB of a body; CloudFront, API Gateway and App Runner default to 16 KB. Setting OversizeHandling to MATCH on a size rule lets you block larger bodies on paths that never need them.
  • Rate-limit POSTs. A rate-based rule per IP, or per JA4 fingerprint behind an HTTPS listener, slows down repeated attempts. Classmethod published a CloudFormation sample  you can learn from.
  • Cap request size behind the ALB. Set client_max_body_size in nginx or LimitRequestBody in Apache, including for requests without a Content-Length.
Two starter rules for the console's JSON rule editor. Replace /uploads/ with the paths that legitimately accept files:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[
{
"Name": "php2shell-upload-progress-field",
"Priority": 10,
"Statement": {
"ByteMatchStatement": {
"SearchString": "php_session_upload_progress",
"FieldToMatch": { "Body": { "OversizeHandling": "CONTINUE" } },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "CONTAINS"
}
},
"Action": { "Count": {} },
"VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "php2shell-upload-progress" }
},
{
"Name": "multipart-outside-upload-paths",
"Priority": 11,
"Statement": {
"AndStatement": {
"Statements": [
{ "ByteMatchStatement": {
"SearchString": "multipart/form-data",
"FieldToMatch": { "SingleHeader": { "Name": "content-type" } },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "CONTAINS" } },
{ "NotStatement": { "Statement": { "ByteMatchStatement": {
"SearchString": "/uploads/",
"FieldToMatch": { "UriPath": {} },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "STARTS_WITH" } } } }
]
}
},
"Action": { "Count": {} },
"VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "php2shell-multipart-scope" }
}
]
If you changed session.upload_progress.name, match your own field name instead. And while you're in there, set expose_php = Off so you stop advertising PHP in every response header.

Detect and monitor

Memory-corruption exploits usually crash workers several times before they succeed, so worker churn is your earliest warning. After that, look for what an attacker does with a shell: drop files, spawn processes and reach for the instance role.
 
 
SignalWhere to see it on AWS
Multipart POSTs followed by memory-limit errors or bursts of HTTP 500 (Patchstack )ALB HTTPCode_Target_5XX_Count alarms, ALB access logs, AWS WAF logs
PHP workers crashing or restarting without a deployPHP-FPM and Apache error logs shipped by the CloudWatch agent
Unusually large or malformed PHPSESSIDcookies in responses, a sign of memory disclosureReverse-proxy logs, if they record response headers
PHP or the web server spawning shells or new binariesGuardDuty Runtime Monitoring on EC2, EKS and ECS on Fargate, for example Execution:Runtime/ReverseShell and Execution:Runtime/NewBinaryExecuted
New or modified .php files in the web rootFile integrity monitoring, such as Wazuh FIM
Instance role credentials used from somewhere elseGuardDuty UnauthorizedAccess:IAMUser/InstanceCredentialExfiltrationfindings, CloudTrail
Unexpected outbound connections from web tiersVPC Flow Logs, GuardDuty
A CloudWatch Logs Insights query for worker churn across your PHP log groups:
1
2
3
4
fields @timestamp, @logStream, @message
| filter @message like /Allowed memory size|exited on signal|SIGSEGV|SIGBUS|zend_mm_heap corrupted/
| stats count(*) as events by bin(5m), @logStream
| sort events desc
If you run Wazuh, two quick wins. Turn on real-time file integrity monitoring for the web root, and let the vulnerability detection module flag the PHP packages once vendor feeds carry these CVEs.
1
2
3
<syscheck>
<directories realtime="yes" check_all="yes" report_changes="yes">/var/www/html</directories>
</syscheck>
For the look-back that Aikido recommends, start with PHP files changed since June 2026 and compare them against what you actually deployed. Container workloads make this easier: a running task should never hold PHP files that aren't in its image.
1
sudo find /var/www -name '*.php' -newermt '2026-06-01' -printf '%TY-%Tm-%Td %TH:%TM %p\n' | sort
Finally, assume a web shell runs with whatever the instance role allows. IMDSv2 protects against SSRF, not code running on the box, so the real control is a least-privilege role. If you find a compromise, rotate anything that role or the app could read.

Series: AI Security (3 articles)

  1. 3
    Find It, Patch It, Watch It: A Community Checklist for php2shell on AWS This article
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article