PHP shipped emergency fixes for PHP2Shell (CVE-2026-109618 and CVE-2026-109619) on October 21. Both are rated CVSS 9.2, both allow remote code execution without logging in, and they affect PHP 5.4 through 8.5. Public exploits are already out.
The fix is simple: upgrade to 8.5.12, 8.4.27, 8.3.36 or 8.2.35. The hard part is answering three questions quickly:
- Which of my machines are on an affected version?
- Which of my Docker images are?
- Has anyone already tried it against me?
I wrote a small tool, php2shell-check, that answers all three.
Install
pipx install php2shell-check
It's one Python file with no dependencies, so you can also just download php2shell_check.py and run it.
Which machines?
On a server:
php2shell-check check
For a whole fleet, without SSH or network access, feed it an inventory file of host version lines (straight from Ansible facts or a CMDB export):
php2shell-check check --inventory hosts.txt
Exit code 0 means not affected, 1 means affected, 2 means it couldn't tell. There's --json and --sarif too, so it drops into CI and GitHub code scanning.
Which images?
php2shell-check scan-docker # Dockerfiles and compose files here
php2shell-check scan-docker --running # plus running containers
A pinned tag like php:8.3.10 is judged exactly. A floating tag like php:8.4 is flagged as ambiguous, because what it resolves to depends on when you last pulled. An end-of-life tag like php:8.1 is flagged as affected outright, since 7.4, 8.0 and 8.1 get no official release.
Did anyone try?
php2shell-check scan-logs /var/log/modsec_audit.log /var/log/apache2/error.log
It looks for indicators published by Splunk, Abstract Security, Bitdefender and others, and each match says which source it came from. One honest caveat: plain access logs rarely record request bodies, so a clean access-log scan isn't an all-clear. The strongest signals need a WAF or ModSecurity log. And it flags attempts, not confirmed compromise.
What it won't do
It has no remote scan mode. Some PHP2Shell checkers probe a URL; I left that out on purpose, so you can't point it at a server you don't own, and running it is never itself an attack. If you want to check a server, run it on that server.
Fix it
php2shell-check harden
prints the upgrade commands for your OS. harden --apply can also write a drop-in ini that disables session.upload_progress, which blocks the known path but is only a partial mitigation. Patching is the real fix.
Code, MIT licensed: https://github.com/SirjanSingh/shiny-php
PyPI: https://pypi.org/project/php2shell-check/
If the log indicators give you false positives, open an issue. That's the part I most want to get right.
Top comments (1)
servers, images, logs. one sweep for the whole footprint.