~/blog/php2shell-cve-2026-109618-cve-2026-109619-mitigation.md
PHP2Shell: what CVE-2026-109618 and CVE-2026-109619 are and how to protect your PHP servers
Two critical PHP flaws allow unauthenticated remote code execution, before your application even runs. Which versions are affected, who is exposed, what Cloudflare's WAF covers, how to update and what to mitigate in the meantime.
- author
- Alejandro Peñaloza
- Published
- Reading
- 12 min
- Español
- Leer en español →

In this article
Updated October 21, 2026 (afternoon): PHP published official backports for 7.4, 8.0 and 8.1 (explained below), expanded its mitigations (container memory limits, attacker-reachable fatal errors, requests without
Content-Length) and clarified thatmemory_limit=-1doesn't fully close the flaw. I also added the exploitation status according to Check Point and Patchstack.
On October 21, 2026, at 14:00 UTC, the PHP team shipped emergency releases fixing two critical vulnerabilities that the community has nicknamed PHP2Shell. Details leaked before the coordinated disclosure, public proofs of concept are circulating, and Cloudflare reports attack traffic against its customers. If you run any PHP server that handles web traffic, this is a today problem.
This post summarizes what official sources say, who is affected, how to update, what Cloudflare's WAF covers, and what to do if you can't update right away.
Quick summary
| CVE-2026-109618 | CVE-2026-109619 | |
|---|---|---|
| Official name | RCE via OOM corruption | RCE via stale global state |
| Severity | Critical, CVSS v4.0 9.2 | Critical, CVSS v4.0 9.2 |
| Impact | Unauthenticated remote code execution | Unauthenticated remote code execution |
| PHP advisory | GHSA-w8hw-w9fx-j79w | GHSA-6wmf-4rv5-p3rp |
| NVD | CVE-2026-109618 | CVE-2026-109619 |
The leaked posts claimed CVSS 10.0; the official score is 9.2, just as critical in practice.
Fixed versions: PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 (official announcement).
PHP 8.1, 8.0 and 7.4: no new release, but the PHP security team published minimal source backports. I explain below what that means in practice.
PHP 7.3 and older: PHP's advisory doesn't mention them. Some reports say the flaw affects everything since PHP 5, without official confirmation: assume they are vulnerable and unpatched.
What PHP2Shell is
This is not a framework or library bug: it lives in the PHP engine itself, in how it handles memory and per-request state. That is why it affects Laravel, Symfony, WordPress and plain PHP alike.
CVE-2026-109618: corruption after running out of memory
According to PHP's advisory, when PHP runs out of memory (OOM) it can be interrupted while internal request data is only partially initialized. Request cleanup may then treat that invalid data as valid. Parsing of multipart/form-data requests is named as one of the triggers.
CVE-2026-109619: global state that outlives the request
Some request-owned internal state can survive after its backing memory has been freed. A later request handled by the same worker may use that stale state before normal initialization resets it. The confirmed paths go through session.upload_progress and, on PHP 8.5, through fatal error backtraces.
Both were fixed in the core with a single change, described in the changelog as "Avoid state corruption via bailout".
Who is exposed
PHP says the risk applies to every installation serving web requests:
- PHP-FPM (behind Nginx, Caddy, etc.)
- Apache with
mod_php php-cgi- FrankenPHP
- LiteSpeed
PHP rates the risk highest for mod_php, where there is a demonstrated exploit in which an unauthenticated request triggers the attack before any of your application code runs. In other words, it doesn't matter how well written your code is, or what authentication middleware you have.
Command-line scripts (php CLI, queues, cron) don't serve web requests and are not listed as a vector, but it's the same binary: when you update, update everything.
Is it being exploited?
Sources still don't agree, so here they are as reported:
- Cloudflare says it observed attack traffic targeting its customers, with attackers mutating the payload to try to bypass its rules (Cloudflare blog).
- watchTowr reports exploitation observed since October 21 in its honeypot network (FAQ).
- Patchstack also reported active exploitation, without publishing details.
- Check Point has no reports of in-the-wild exploitation so far (Check Point blog).
- NCSC-NL has no indication of active exploitation, but confirms public proofs of concept exist (repository).
- CISA KEV: at the time of this update, neither CVE is in the catalog.
With a public exploit and a flaw that needs no authentication, assume mass scanning has already started.
What to do now: update
It is the only complete fix. Each release announcement says it plainly: there are no complete mitigations, updating is the only safe choice. First check which version is actually running (CLI and FPM can differ):
php -v
php-fpm -vDepending on the distro, the binary may be called php-fpm8.3 or similar. In a container, this tells you what is serving web traffic:
docker compose exec app php -r 'echo PHP_VERSION, PHP_EOL;'You need 8.5.12, 8.4.27, 8.3.36 or 8.2.35 or later within your branch.
Debian and Ubuntu
sudo apt update
sudo apt install --only-upgrade 'php8.3*'
sudo systemctl restart php8.3-fpmIf you use mod_php, restart apache2 too. Linux distributions and LTS vendors received the patches in advance, so their packages should be available. If you use your distribution's packages (rather than Ondřej Surý's PPA or similar), the version number may not match php.net because distros backport the patch onto their own version. In that case, check your distro's security advisory to confirm the installed package covers CVE-2026-109618 and CVE-2026-109619.
RHEL, Rocky, Alma and Fedora with remirepo
If you rely on rpms.remirepo.net, be careful: it is volunteer-maintained and won't have the update on October 21 itself. PHP recommends switching to distribution packages or building from source so you can patch immediately.
Docker
If your image is based on php:8.x-fpm or similar, restarting is not enough: you have to rebuild on the new base image and redeploy. Pin the minimum safe patch instead of a floating tag:
FROM php:8.3.36-fpmdocker compose build --pull --no-cache
docker compose up -d
docker compose exec app php -v--pull forces Docker to fetch the new base image; without it, Docker may reuse the cached one.
Shared or managed hosting
Check your provider's dashboard or status page: many are already updating, but confirm the version with phpinfo() on a temporary route (and delete it afterwards).
PHP 8.1 or older: what the "backports" mean
PHP's advisory says two things that look contradictory at first:
PHP 8.1 and older no longer receive updates from the PHP project. Some distributions and LTS vendors provide backported fixes, so contact your vendor for details, or upgrade to a supported version.
Given the severity of these vulnerabilities, the PHP security team provided minimal source-backports for PHP 7.4, PHP 8.0, and PHP 8.1 to help people building from source or without access to an LTS vendor.
They don't contradict each other; they talk about different things.
The first sentence says PHP 8.1 and older are end of life: the PHP project won't ship new releases for those branches. There will be no new "PHP 8.1.x" on php.net, no Windows binaries, and no updated official Docker images. For those versions it points you to your distribution or extended support vendor (Debian, Ubuntu Pro, Red Hat, TuxCare, etc.), which may maintain older versions and ship patched packages, or to upgrading to a supported version.
The second sentence is an exception because of the severity. It's not a new release either: it's only the source code patch, the set of lines to change in PHP to fix these two flaws.
- "Minimal" means it fixes only PHP2Shell. Those versions still carry every other vulnerability discovered since they went end of life.
- "For people building from source" means it helps if you compile PHP yourself: you download the PHP 8.1 source, apply the patch and build.
- "Without access to an LTS vendor" means it's meant for people who don't have a vendor building the package for them.
In practice:
| If you run PHP 7.4, 8.0 or 8.1… | What to do |
|---|---|
Installed with your distro's apt or yum |
Check whether your distro shipped a patched package. If not, PHP's patch won't help you directly. |
Official php:8.1 Docker image |
It won't be updated. Upgrade, or build your own image applying the patch. |
| Shared hosting | It depends on the provider: ask them to update or move you to a newer version. |
| You compile it yourself | Apply the backport and rebuild. |
Your options, in order of preference:
- Move to a supported branch: that's the real fix.
- Use a patched package from your distribution or an extended support vendor, if one exists.
- If you build PHP 7.4, 8.0 or 8.1 yourself, apply the minimal backports from the PHP security team.
- Apply the mitigations below and isolate the server from the Internet. CERT-EU recommends taking unpatchable internet-facing applications off public networks (advisory 2026-018).
And if you upgrade, aim for 8.3 or later: PHP 8.2 also reaches end of life on December 31, 2026.
If your site is behind Cloudflare
Cloudflare took part in the coordinated disclosure alongside the PHP Foundation, OpenAI and Wiz, and deployed WAF rules about 9 hours before public disclosure. Since October 21 at 03:02 UTC, customers using the WAF, including the free plan, have been protected by default (Cloudflare announcement).
| Plan | Where the rules live | What to do |
|---|---|---|
| Free | Cloudflare Managed Free Ruleset | Nothing, they're on |
| Pro, Business, Enterprise | Cloudflare Managed Ruleset | Confirm the deployment is in Block mode |
| All | Cloudflare Managed Response Firewall, rule Default block for High confidence Signatures | Nothing, it's on |
The rules are named Generic Rules - Anomalous Execution (1 to 7). What's new is that they use full transaction detection: the WAF analyzes both the request and the response of the same HTTP exchange, and on a match it can block your origin's response before it reaches the attacker. According to Cloudflare, this allows more accurate rules with fewer false positives than looking at the request alone.
To check whether you were targeted: Security > Security Analytics > Events, filtering by those rule IDs (listed in the announcement).
Two important caveats:
- The WAF does not replace the patch. Cloudflare says so explicitly: the flaw is in the PHP runtime and the WAF is an extra layer. Attackers are already mutating payloads.
- It only protects traffic that goes through Cloudflare. If your origin server accepts direct connections, an attacker who knows its IP bypasses the WAF. Restrict the origin to Cloudflare's IP ranges, or use Cloudflare Tunnel or Authenticated Origin Pulls.
Temporary mitigations (if you can't update today)
PHP's advisory is clear: these measures reduce exposure to known attack paths but do not make an installation safe. Gina Peter Banyard, from the PHP team, stressed the same point: disabling session.upload_progress is a partial mitigation that does not stop exploitation. They buy you hours, not a permanent solution.
Apply everything in php.ini, the FPM pool or the server configuration, never with ini_set() or .user.ini: they must take effect before PHP parses the request. And restart every worker afterwards.
Against CVE-2026-109619
session.upload_progress.enabled = 0It is on by default and almost no application uses it (it powers session-based upload progress bars). This blocks the confirmed paths, not every variant.
On PHP 8.5, also add:
fatal_error_backtraces = 0The updated advisory adds two more measures:
- Remove invalid or deprecated settings applied per request that emit diagnostics before the request finishes initializing. Hiding those diagnostics with
error_reporting,display_errorsor@does not help: you have to remove the setting that triggers them. - Fix attacker-reachable fatal errors and execution timeouts, including failures in callbacks, upload or import processing, and cleanup.
Against CVE-2026-109618
PHP recommends removing the per-script memory limit to avoid the controllable OOM condition that triggers the flaw, set where applications can't override it. In the FPM pool (for example /etc/php/8.3/fpm/pool.d/www.conf):
php_admin_value[memory_limit] = -1In Apache with mod_php:
php_admin_value memory_limit -1Make sure no other pool, directory or application file sets a finite limit again. And keep in mind what the advisory adds: this removes the failure caused by PHP's memory limit, but an underlying system allocation failure can still reach the vulnerable path.
With no limit, a runaway script can also eat all the server's RAM. For that, PHP recommends a container or cgroup memory limit as an availability boundary, ideally with a policy that kills only the affected worker. With Docker Compose, for example:
services:
app:
mem_limit: 1gCombine it with request size limits:
post_max_size = 8M
upload_max_filesize = 6MUse 8M if you don't accept file uploads and 32M if you do, always keeping upload_max_filesize below post_max_size.
PHP also suggests limiting, at the web server or proxy, the decoded request size, the number of fields and multipart parts, headers and nesting depth. Those limits must also cover requests without a usable Content-Length (for example, chunked transfers). In Nginx, client_max_body_size applies to those requests as well:
client_max_body_size 8m;
large_client_header_buffers 4 8k;And don't use RLIMIT_AS or RLIMIT_DATA (for example via ulimit or systemd) to contain memory: they can trigger exactly the vulnerable allocation failure.
Check that the mitigation is live
CLI and FPM read different files, so check from the web context. A temporary route with:
<?php
var_dump(
ini_get('session.upload_progress.enabled'),
ini_get('memory_limit'),
ini_get('post_max_size'),
);Delete it as soon as you've confirmed.
Detection: what to look for on your servers
There are no reliable public signatures or indicators of compromise yet, so detection is behavioral. The suspicious pattern is a PHP process launching other things:
- Parent processes
php-fpm,php-cgi,httpdorapache2startingsh,bash,python,perl,curl,wgetornc. - Command lines containing
/dev/tcp/,fsockopenormkfifo(typical of reverse shells). - Reconnaissance commands run by the web server user:
whoami,id,uname -a.
To list PHP or Apache children that shouldn't exist:
ps -eo pid,ppid,user,cmd --forest | grep -E -A3 'php-fpm|apache2|httpd' | grep -E 'sh|bash|curl|wget|nc|python|perl'And new PHP files in the web root over the last 3 days:
find /var/www -name '*.php' -mtime -3 -lsIf you find something: isolate the server, preserve web server and PHP logs before touching anything, and hunt for web shells, new files in the web root, cron jobs and new accounts. In MITRE ATT&CK this maps to T1190 (exploit public-facing application) and T1505.003 (web shell). Updating PHP on an already compromised server does not remove the attacker: if there was a compromise, rebuild from a clean image and rotate credentials.
Checklist
- Inventory every server, container and hosting account running PHP (including the forgotten ones: staging, demos, the marketing WordPress).
- Confirm the version serving web traffic, not just the CLI one.
- Update to 8.5.12, 8.4.27, 8.3.36 or 8.2.35 (or your distro's patched package).
- If you use remirepo, switch to distro packages or build from source.
- Rebuild Docker images with
--pulland pin the minimum patch in theDockerfile. - Restart FPM, Apache and every worker.
- If you use Cloudflare: rules in Block mode and origin closed to direct connections.
- Where you can't update today:
session.upload_progress.enabled = 0, container memory and request limits, review attacker-reachable fatal errors, and isolate from the Internet. - Review PHP child processes and new files in the web root.
- Plan your exit from PHP 8.2 (end of life December 31, 2026) and from 8.1 or older.
References
- PHP: security advisory and announcement of 8.5.12, 8.4.27, 8.3.36 and 8.2.35 and PHP 8 changelog
- GitHub Security Advisories: GHSA-w8hw-w9fx-j79w (CVE-2026-109618) and GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
- NVD: CVE-2026-109618 and CVE-2026-109619
- Cloudflare, WAF protections against php2shell
- CERT-EU, security advisory 2026-018
- NCSC-NL, PHP2Shell repository
- Check Point, what we know about php2shell
- Tenable, php2shell FAQ
- watchTowr, PHP2Shell FAQ
- SOCRadar, PHP2Shell analysis
- Sansec, PHP2Shell research
- Huntress, analysis of the leaked vulnerabilities
- SecurityWeek, coverage of the leak
- PHP manual: Session Upload Progress and FPM configuration
Written on October 21, 2026 and updated the same day with the information available. Things are moving fast: check the official advisories before making decisions.
Comments
No comments yet. Want to leave the first one?
Leave a comment
$ ls ~/blog | grep related
Keep reading

War log: debugging CORS, refactoring my own code and integrating Webpay without losing the afternoon
Real problems and how I solved them: a CORS error that only failed in production, a memory leak on the server, my own year-old code refactored without shame, and a practical guide to integrating Webpay Plus, Mercado Pago and Flow.

Developer productivity: VS Code, advanced Git, agile project management and automation scripts
My daily workflow as a developer: the VS Code extensions and settings that actually help, Git beyond commit and push (rebase, cherry-pick and bisect), how to manage small projects without bureaucracy, and the scripts that automate the repetitive work.

From localhost to production without tears: VPS, DNS, hosting and Docker for developers
A practical guide to taking a project to production: how to secure a VPS from scratch, set up DNS and Cloudflare, choose between shared hosting, a VPS or the cloud, and spin up your local database with Docker Compose in seconds.