If you run Apache with mod_php, an unauthenticated HTTP request could get code execution on your server before a single line of your application ran. PHP's own advisory says so. The php2shell fix for CVE-2026-109618 and CVE-2026-109619 shipped on 21 October 2026 as PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. What's left is making sure the fixed binary is the one actually answering your web requests. Running php -v in a shell won't tell you that.
A correction first, because it went viral. These are not "CVSS 10.0". That number came from a Tell HN post whose author gave the source as "trust me, bro." The PHP security team scored both bugs CVSS v4.0 9.2 with vector AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H, which means network-reachable, no auth, high complexity. Red Hat's CVSS 3.1 score is 8.1. Neither number is a reason to wait.
What php2shell actually is
Both bugs live in PHP core, underneath whatever WordPress plugins or Composer packages you run. Here's how the php.net advisory and the two GitHub advisories describe them.
CVE-2026-109618: RCE via OOM corruption
Advisory: GHSA-w8hw-w9fx-j79w. On an out-of-memory error, PHP longjmp()s out of the current context. If that happens while request memory is reachable but only partly initialized, request shutdown later reads that garbage as valid state. The advisory lists cheap ways for an attacker to make memory run out: multipart bodies with no usable Content-Length slip past the first post_max_size check, multipart parts can carry unlimited headers, and empty file controls build metadata arrays without counting toward max_file_uploads. The fix replaces the longjmp() with a delayed soft OOM at the next VM safe point plus a hard _exit() at a higher threshold.
CVE-2026-109619: RCE via stale global state
Advisory: GHSA-6wmf-4rv5-p3rp. Persistent workers keep global error, session and extension state that can point into the previous request's freed heap. Request A leaves that state behind through a fatal error, an OOM or a diagnostic emitted during shutdown. Request B, on the same worker, reads it before normal init resets it, and that's a use-after-free. The confirmed cases are PHP 8.5's fatal_error_backtraces and the session upload progress lifecycle.
The reporters credited on both advisories include accounts whose names point to Wiz (sagitzadik-wiz), Areion (tomash-areion) and OpenAI (amyb-oai).
Which setups are exposed: mod_php vs PHP-FPM vs the rest
Every branch below the fixed releases is affected. How exposed you are depends on the SAPI, and the advisories go into detail on each one:
| SAPI | CVE-2026-109618 | CVE-2026-109619 |
|---|---|---|
| Apache mod_php | Confirmed, full unauth RCE with default limits and prefork | Confirmed |
| PHP-FPM | Susceptible per source analysis, no full exploit shown | Session upload progress path validated |
| FrankenPHP | Likely, similar to mod_php | No early consumer found by default, not immune |
| LiteSpeed (LSAPI) | Likely, unvalidated | Conditional |
| php-cgi | Listed at risk by php.net | Listed at risk by php.net |
So mod_php goes to the front of the queue. Red Hat points out that standard RHEL deployments use FPM and calls RCE there difficult in practice. Fine, schedule the FPM boxes second. Just don't drop them off the list.
Fixed PHP versions per branch
| Branch | Fixed upstream |
|---|---|
| 8.5 | 8.5.12 |
| 8.4 | 8.4.27 |
| 8.3 | 8.3.36 |
| 8.2 | 8.2.35 (EOL 31 Dec 2026) |
| 8.1 and older | No release. Minimal source backports for 7.4, 8.0, 8.1 |
Is php2shell exploited in the wild? Depends who you ask
Word of the bugs leaked on 20 October. Per Hadrian's timeline, the Tell HN post went up around 15:20 UTC, and PHP moved the release forward to 21 October at 14:00 UTC. The name comes from the leaked exploit tool, "PHP2Shell", a screenshot of which has been making the rounds. Since then the reports have pointed in both directions:
- Hadrian, 11:35 UTC on 21 Oct: "We are now seeing php2shell exploited in the wild at scale," mostly through the session upload progress path.
- watchTowr says it is observing exploitation attempts against both CVEs. It gives no counts.
- Cloudflare's CTO reportedly said they were seeing early attacks. I only have that second-hand, through Hadrian.
- Huntress had seen no exploitation across its partner base as of 10am ET on 21 Oct.
- CISA's alert says: "CISA has not observed exploitation of these vulnerabilities as of the time of publication."
"Attempts" and "compromises" are different claims. I'm not going to wait for KEV to settle the argument.
Step 1: check the PHP version your web server actually runs
The CLI and the web SAPI often use different binaries and different php.ini files. To see what the web actually runs, drop a throwaway file in the docroot, request it once, then delete it:
<?php echo PHP_VERSION, ' ', PHP_SAPI, ' ', php_ini_loaded_file();Then check the real processes:
apachectl -M 2>/dev/null | grep -i php # mod_php loaded?
ps -eo cmd | grep -E 'php-fpm: master' # which FPM master is running
php-fpm8.3 -v # that FPM's version, adjust per branchDistro packages don't follow the upstream numbering. Ubuntu 24.04 fixes this in php8.3 8.3.6-0ubuntu0.24.04.13, and PHP_VERSION still starts with 8.3.6. If you grep for "8.3.36" on that box you'll conclude it's still vulnerable after you patched it. Check the package version instead:
dpkg -l 'php*' | awk '/^ii/{print $2, $3}'
rpm -q --changelog php-common | grep -m3 'CVE-2026-10961'On Docker, check inside the image you actually deploy. The tag won't settle it, since your image is only as fixed as the last time you rebuilt it:
docker run --rm --entrypoint php your-app:prod -r 'echo PHP_VERSION, PHP_EOL;'Step 2: install the patched PHP packages
- Ubuntu: USN-8948-1/-2. 22.04, 24.04, 26.04 and 26.10 get it under standard support, and 20.04 and older need Pro ESM. Run
sudo apt update && sudo apt install --only-upgrade '?name(^php[0-9.]+$)'. - Debian (tracker): trixie is fixed in
php8.4 8.4.27-1~deb13u1(DSA-6574-1) and bookworm inphp8.2 8.2.35-1~deb12u1(DLA-4848-1). Sid was still unfixed when I checked. - Sury (packages.sury.org, Ondřej's repos) is reported to ship 8.5.12, 8.4.27, 8.3.36 and 8.2.35, plus backports for 8.1, 8.0, 7.4, 7.3, 7.2, 7.1 and 5.6.
- Remi: php.net says plainly that
rpms.remirepo.net"will not have updates today" and recommends switching to distro packages or building from source. That's rough if your RHEL-family servers pull PHP from that one volunteer-maintained repo. - RHEL/AlmaLinux: Red Hat had no fixed packages when AlmaLinux posted on 21 Oct. Alma was backporting to its module streams and said its builds would ship ahead of RHEL.
- Docker official images: the library file lists 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Run
docker pull, rebuild with--pull, redeploy. - Managed hosting: ask your host for the exact package version. "We're aware" doesn't tell you anything.
Then restart. FPM and mod_php keep the old code mapped in memory until the workers die, so a box with the new package installed and the old workers still running is unpatched.
If you can't patch today
PHP is blunt about this: none of these settings make you safe, they just cut off the known paths.
; set in php.ini or php_admin_value, never ini_set() or .user.ini
session.upload_progress.enabled = 0
memory_limit = -1 ; partial: closes the controllable OOM path
post_max_size = 8M ; 32M if you need uploads
upload_max_filesize = 2M ; keep below post_max_size
fatal_error_backtraces = 0 ; PHP 8.5 onlyRestart every worker after the change. At the proxy, cap request size, multipart part count and header count, including for requests with no usable Content-Length. Don't reach for RLIMIT_AS/RLIMIT_DATA, because they can cause the vulnerable allocation failure. pm.max_requests=1 narrows 109619 if you can take the performance hit.
PHP 8.1, 7.4 and other EOL versions
If you're on 8.1 or older, you're relying on your vendor, Sury's backports or PHP's source-only patches for 7.4/8.0/8.1. CISA's advice for anything that can't be patched is to take it offline. For an internet-facing PHP 7.4 box I agree.
Patch first, then read your logs. Huntress suggests watching for php-fpm, httpd or apache2 spawning sh, curl or nc.
How long does a PHP security patch take to reach your production servers: hours, days, or "whenever the base image updates"? Be honest in the comments.
