php2shell: What to Do if Your Servers Run PHP
Two critical PHP flaws, php2shell, got fixed on Oct 21. A triage order for anyone with PHP on a server: inventory, patch and restart, mitigate, isolate.
In this post
On October 21, 2026, the PHP project released 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to fix two vulnerabilities, CVE-2026-109618 and CVE-2026-109619, that php.net says “can allow unauthenticated remote code execution.” Both score 9.2 on CVSS v4.0. CISA calls the pair “php2shell.” The release was scheduled for 14:00 UTC, and Cloudflare says the vulnerability “was leaked before the official release date”.
This is the order I would work in if I were handed a company’s servers today. The mechanism is in php.net’s advisory and the two GHSAs, so I’ll only cover what you need to make decisions.
Who is exposed
php.net’s advisory names the exposure as “all PHP installations serving web requests, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed.” It rates mod_php highest, with “a demonstrated exploit where an unauthenticated request can trigger the attack before any application code runs.”
For PHP-FPM the GHSA for CVE-2026-109618 says “no complete FPM exploit has been demonstrated.” The GHSA for CVE-2026-109619 describes a path that was validated against FPM, using session upload progress. So FPM is exposed, just ranked below mod_php.
My scoping goes a little past what the advisories say:
- Patch or isolate now: anything that answers HTTP through PHP, whatever address php-fpm listens on. A pool on 127.0.0.1 or a unix socket behind nginx still handles every request nginx forwards. That includes internal tools, admin panels, staging and forgotten vhosts.
- Installed but not serving requests: a CLI-only install is outside the path these advisories describe. Remove it if nothing needs it, but treat that as hygiene, separate from php2shell.
CISA warns that “PHP is often a component of other software and prevalence may not be directly observable.” The NCSC says to check appliances that bundle PHP.
Is it being exploited?
None of these sources claims a confirmed compromise.
| Source | What it says |
|---|---|
| watchTowr | “Exploitation has been observed in the wild as of the 21st of October,” from its honeypot network. No time or indicators given. |
| Cloudflare | Saw “attack traffic targeting our customers” and attackers changing payloads to evade its protections. |
| CISA alert | “CISA has not observed exploitation of these vulnerabilities as of the time of publication,” and adds that “they pose a risk to organizations if left unpatched.” |
| CISA SSVC and KEV | In the CVE records, CISA’s SSVC entry (14:34 UTC, Oct 21) says Exploitation: none. Neither CVE is in the KEV catalog as of version 2026.10.21, which I re-checked at 18:08 UTC on Oct 21. |
Someone is probing, and Cloudflare says payloads are being tuned against its rules. Nobody has published a victim. php.net already says to update immediately, so I wouldn’t wait for KEV.
Triage, in order
1. Inventory every host with PHP
List what is running, then what is installed. This works on Debian
and Alpine without ps. Run it as root:
grep -H . /proc/[0-9]*/comm | grep -E 'php|franken'Then find the binaries and Apache modules, including ones no package manager knows about:
find / -xdev -type f \
\( -path '*bin/php*' -o -path '*bin/frankenphp' \
-o -name 'libphp*.so' -o -name 'mod_php*.so' \) \
2>/dev/nullPHP runs inside the apache2 processes under mod_php, so there is no
php process name to grep for. This finds processes that
have the module loaded:
grep -lE 'libphp|mod_php' /proc/[0-9]*/maps 2>/dev/nullI tested it in a throwaway php:8.3-apache container. It
listed the Apache master and not the workers, because workers that
dropped privileges have unreadable maps, even to root inside a
container. The master is enough to tell you the host runs mod_php.
And the installed package versions:
# Debian, Ubuntu
dpkg -l 'php*' | awk '/^ii/ {print $2, $3}'
# Alpine
apk info -v | grep '^php'I ran these in containers on my Arch machine (Debian trixie, Alpine
and the official php images). They found the FPM workers, the binaries,
libphp8.4.so and mod_php84.so.
-xdev stays on one filesystem, so run it again against
mounted volumes. Each container has its own root. The find
also matches helpers like phpize; ignore those.
Split what you find into two lists: hosts where PHP serves requests go to step 2 today; CLI-only installs go on a cleanup list.
Check container images too. Rebuild them from a fixed base, because the PHP inside an image is whatever it had when it was built.
2. Patch, then restart
Update to 8.5.12, 8.4.27, 8.3.36 or 8.2.35. php.net says Linux distributions and LTS vendors got the patches in advance, so use your vendor’s package.
Check the version after you install. At about 18:00 UTC on October
21, the Debian trixie repo gave me
php8.4-fpm 8.4.27-1~deb13u1. The Alpine 3.24 repo that the
php:8.3-fpm-alpine image uses still gave me
php84-fpm-8.4.26-r0 when I checked again at 18:09, one
release short of the fix. The official php:8.3-fpm image
was already 8.3.36. Check the version string after every upgrade.
Then restart. A running process keeps the code it started with, so reload FPM or restart Apache after the package install. This lists processes still mapped to a replaced PHP binary or library:
grep -lE '(php|franken).* \(deleted\)' \
/proc/[0-9]*/maps 2>/dev/nullI tested it by replacing the php-fpm binary under a running master on Debian. Before the replace it printed nothing, and after it printed the master’s PID. A restart makes that list empty. It has the same limit as the mod_php check: it can’t read the maps of workers that dropped privileges, so it reports the master. It also tells you whether your distro package restarted the service itself.
3. If you can’t update yet, apply the temporary mitigations
php.net lists settings under “If you cannot update immediately.” They “reduce exposure to specific attack paths but do not make an installation safe.” This is the subset I’d apply to an FPM pool while the patch gets scheduled. I tested it on Debian with PHP 8.4.27 and a script that printed back the effective values:
; FPM pool file, then restart php-fpm
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[session.upload_progress.enabled] = 0memory_limit=-1removes the controllable memory-limit failure, and php.net says to set it, preferably where applications can’t override it. An underlying allocation failure can still reach the vulnerable path, so check that no pool, directory or app setting puts a finite limit back.post_max_sizeof 8M for apps with no uploads, 32M with uploads, withupload_max_filesizebelow it. php.net also suggests limits at the web server or proxy on request size, multipart part count and header count.session.upload_progress.enabled=0goes in php.ini, the FPM pool or server config, notini_set()or.user.ini, and every worker needs a restart. It blocks the confirmed session paths and no other variants.- On PHP 8.5,
fatal_error_backtraces=0blocks the confirmed fatal backtrace path only. - For a memory boundary, use a container or cgroup limit. php.net says
RLIMIT_ASandRLIMIT_DATAcan cause the vulnerable allocation failure.
4. If mitigations aren’t enough, isolate or take it offline
The UK NCSC sets the sequence this post follows: update, then the PHP team’s temporary mitigations. If you can’t apply those, or it would take too long, “you should consider taking the system offline until it can be fully mitigated.” It also says to assess a system you can’t update or disable for compromise, and to look for backdoors.
The NSA, in a post on X reported by The National, told organizations to be prepared to update, and to take a system offline only if an immediate update isn’t feasible, until it can be fully mitigated. I couldn’t read the NSA’s own page, so this is The National’s copy.
CISA’s alert goes further. It says to patch immediately, prioritizing internet-facing systems, and that organizations “that cannot update immediately, or that continue to use PHP 8.1 or earlier unsupported versions, should take PHP-based systems offline until they can be patched or updated.” It adds that a web application firewall “may provide some protection.”
For internal tools, I’d use an allow list so only a few known addresses can reach them. For a public site that can’t be patched, CISA says to take it offline, and the NCSC says to consider it if you can’t apply the temporary mitigations. The National reports that the Netherlands, Finland, France and Australia temporarily took government services offline to apply the patch. Cloudflare says its WAF customers have been protected by default since 03:02 UTC on October 21, which helps, though attackers are already tuning payloads against it.
5. End-of-life branches
W3Techs, Oct 21 has 7.7% of PHP sites on PHP 5, 27.0% on PHP 7 and 0.1% on PHP 4. Its PHP 8 breakdown has 5.8% of 8.x sites on 8.0 and 15.2% on 8.1, and PHP 8 is 65.2% of PHP sites. So (7.7 + 27.0 + 0.1) + (5.8 + 15.2) x 0.652 comes to about 48.5% of PHP websites on a branch with no php.net release. That is my arithmetic. W3Techs counts websites, not servers, and a distro can backport a fix to a dropped branch.
Per the php.net advisory, PHP 8.1 and older no longer get updates from the project. It did publish “minimal source-backports” for 7.4, 8.0 and 8.1 “to help people building from source or without access to an LTS vendor.” Per php.net/eol.php, 8.1 ended December 31, 2025, 8.0 in November 2023 and 7.4 in November 2022. 5.x and 7.0 through 7.3 have nothing from the project. 8.2 stops getting security fixes on December 31, 2026 (supported versions), so it is patched now and on a clock.
The CVE records list only 8.2 through 8.5 as affected. Cloudflare says “all web servers running PHP 5.4 or later,” and the NCSC says “Organisations using any version of PHP are affected.” Treat every older branch as exposed. The project built backports for three of them.
Your options for those servers, in the order I’d pick:
- Upgrade to a supported branch.
- Vendor support. On RHEL, Ubuntu Pro or a commercial PHP vendor, ask for the fixed package today and get the version number in writing.
- Build from the backport branches, if you have people to own a source build of PHP indefinitely.
- Offline, where CISA puts anyone on 8.1 or earlier without a fix.
Updating WordPress doesn’t fix it
If your PHP sits under WordPress or Magento, the admin panel can’t fix this. Cloudflare: “These flaws are in the PHP runtime, so updating website code or a plugin(s) alone does not fix these vulnerabilities.” On managed hosting, ask the host which PHP version your site runs and when it was restarted on the fixed build.
What to tell your board or a customer
A customer’s security team will ask, and so will your board. Here is the paragraph I’d start from, with the brackets filled in from your inventory:
On October 21 the PHP project released fixes for two critical remote code execution vulnerabilities (CVE-2026-109618 and CVE-2026-109619). We identified [N] systems that serve requests through PHP, including [internal and staging systems]. [N] are patched and restarted on the fixed versions as of [date and time]. [N] cannot be patched yet because [reason]; those are [isolated / offline] until [date]. CISA reported no observed exploitation at the time of its alert, while Cloudflare and watchTowr report attack traffic and exploitation, so we are reviewing logs for PHP workers launching shells. We will confirm completion on [date].
If the reviewer sends a questionnaire next, I wrote about how those reviews go for AI startups.
The other trust incident this week
On October 17 it was eMudhra’s CA keys, and I wrote up what that did to Elixir apps that trust a bundled CA store. I found no source tying the two together. In both, central fixes came out within hours, and the exposure is in whatever nobody pushes updates to: a static CA bundle in one case, an old PHP branch in the other.
If you’d like someone to run this triage with you, or to answer the customer’s security questionnaire that follows, that is what the Security Review Sprint and the Security Leadership retainer on my consulting page are for.