PHP2Shell: what CVE-2026-109618 and CVE-2026-109619 are, and how to confirm you're fixed
Two unauthenticated remote code execution bugs in the PHP interpreter, exploited before the fix shipped. What they are, how to patch, and how to confirm the fix on each host.
On Tuesday 20 October, details of two remote code execution bugs in PHP leaked before their planned disclosure on the 22nd. PHP moved its release forward a day. On Wednesday 21 October, at around 14:00 UTC, it published 8.5.12, 8.4.27, 8.3.36 and 8.2.35. By that afternoon watchTowr was reporting exploitation attempts against both bugs. Cloudflare had already seen attacks that morning, before a fix existed.
The pair is being called PHP2Shell. Both are rated 9.2 under CVSS v4.0. The 10.0 that circulated with the leak was not PHP's score. An attacker can reach either bug with an ordinary web request and no login. Both sit in the interpreter itself, not in WordPress, Laravel or anything else running on top. If a server of yours runs PHP and answers HTTP requests, this applies to you.
What the two bugs are¶
CVE-2026-109618 is in PHP's out-of-memory handling. If PHP runs out of memory at the wrong moment while setting up a request, some internal request data is left half-built. The cleanup code that runs afterwards doesn't know this and treats the data as valid. An attacker who can control when memory runs out can turn that into code execution.
CVE-2026-109619 is a use-after-free: state that belongs to one request outlives the memory behind it. One confirmed attack path goes through session upload progress. That feature is on by default, and it touches session handling before PHP has finished setting up the request.
Every PHP release from 5.4 up to 8.5.11 is affected. So is every way of running PHP for the web: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. PHP considers mod_php the worst case. There, the bug can fire before your application code has even loaded, so nothing you wrote can stand in the way.
How to remediate¶
Upgrade. That is the fix, and PHP's own advisory says there is no complete mitigation.
If you build PHP from upstream source, move to 8.5.12, 8.4.27, 8.3.36 or 8.2.35 and restart every worker.
If PHP comes from your distribution, install the vendor's updated package. Don't rely on the version number, because distributions backport fixes and often leave it unchanged. Check the package changelog for the CVE IDs instead:
# Debian / Ubuntu
apt changelog php8.2-fpm | grep -E 'CVE-2026-10961[89]'
# RHEL / Alma / Rocky
rpm -q --changelog php | grep -E 'CVE-2026-10961[89]'
If PHP runs in containers, rebuild from a patched base image and redeploy. A running container does not update itself.
If you can't patch tonight, there are two partial measures:
- For CVE-2026-109619: set
session.upload_progress.enabled = 0and restart every worker. On PHP 8.5, also setfatal_error_backtraces = 0. This closes one known attack path and does nothing at all for CVE-2026-109618. - For CVE-2026-109618: move memory-limit enforcement from PHP to the server level and keep upload sizes low. This narrows the bug but does not close it. It is also a change for whoever runs the server, not for the application team.
PHP 8.1 and older are past end of life and get no upstream fix. If you run one of them from a distribution with extended support, check whether the vendor has backported the patch. If you built it yourself, or your host doesn't say, assume you are vulnerable. This is the uncomfortable part of PHP2Shell: the exposure is largest on the systems nobody has touched in years.
How you know you're fixed, and what a test can't tell you¶
Checking from the outside is harder than it looks. An X-Powered-By: PHP/8.1.2
header doesn't prove a server is vulnerable, because a backported fix keeps the
old version string. A missing header doesn't prove it's safe either, since
expose_php may simply be off. The reliable check happens on the host itself:
the package changelog, php -v for upstream builds, and confirming that every
worker restarted after the upgrade.
Nobody should confirm the fix by running the exploit against production. These are memory-corruption bugs, and a failed attempt can crash workers or leave a process in a state no one wants to debug on a Thursday night. We don't do it in Pentr, and we wouldn't recommend a tester who does.
What an external test does well is answer the question that eats the first day of any advisory: where is PHP running at all? A Pentr run lists the in-scope hosts that expose PHP and flags the end-of-life versions and risky configurations it can see from outside. Each engagement gets its own sealed evidence locker. The run before you patch and the retest after it give you two signed, independently verifiable records: one showing the exposure, one showing it closed. That pair is what you hand to a customer or an auditor who asks when it was fixed.
If you ship software that bundles PHP, the Cyber Resilience Act applies too. Since 11 September 2026, manufacturers must send an early warning within 24 hours of becoming aware of an actively exploited vulnerability in their product, and a fuller notification within 72 hours. Whether PHP2Shell triggers that duty depends on your product and on how the bug is being exploited. Either way, a dated record of when you found the exposure and when you closed it is what that clock is measured against.
Could a pen test have caught PHP2Shell before Tuesday? No. Researchers at Wiz found these bugs by digging into the interpreter, and no external test of your application would have found them. What regular testing changes is your starting position. If you already knew which hosts ran PHP and which were still on 7.4, Wednesday was a patching day rather than a search.
If you do one thing tonight, run the changelog check above on every PHP host, and turn off upload progress on any host you can't upgrade yet.