PHP2Shell exposure check
Find out whether your PHP servers are exposed to the PHP2Shell flaws, and what to do about it. Run a local check for a definitive answer, or check a URL for a first signal.
php -v can mislead you.Check your exposure
Three ways in. Local checks are definitive; the URL check only reads what a server says about itself.
Collect evidence on the server
Read-only scripts. They change nothing and send nothing anywhere: you copy the output yourself.
Finds CLI, PHP-FPM, LiteSpeed lsphp, cPanel EA-PHP, Remi and Plesk builds plus Apache mod_php, reads each one's package version, and works out which ones are actually in use: running processes, Apache workers with PHP loaded, cron and PATH. Run it with sudo so it can see other users' processes. Review it first.
curl -fsSLO https://scan.theadminstack.com/php2shell/scripts/php2shell-check.sh less php2shell-check.sh # review it sudo bash php2shell-check.sh
Finds every PHP build, then works out which one is actually in use: IIS handler mappings (server, site and web.config), Apache, Windows services, running processes, scheduled tasks and PATH. Old copies that nothing references are reported as not in use. Run it from an elevated PowerShell: without admin rights it can't read IIS's configuration. Add -Path for non-standard locations.
Invoke-WebRequest https://scan.theadminstack.com/php2shell/scripts/php2shell-check.ps1 -OutFile php2shell-check.ps1 notepad .\php2shell-check.ps1 # review it # from an elevated (Run as Administrator) PowerShell: powershell -ExecutionPolicy Bypass -File .\php2shell-check.ps1 | Set-Clipboard
No shell? Upload this file to your web root and open it in a browser. It reports the PHP that actually serves your site. Each download carries a new random key and stops working 60 minutes after upload. Delete it once you have the output.
On Ubuntu, Debian and RHEL hosts the drop-in can't see package versions. If it reports a version a distro might have backported, ask your host or run the Linux check.
Checks every running container with docker exec. Official php:* images report PHP_VERSION; distro-based images report their package. Add --images to also check local images (offline, throwaway containers). Works with Podman: DOCKER=podman.
curl -fsSLO https://scan.theadminstack.com/php2shell/scripts/php2shell-docker.sh less php2shell-docker.sh # review it bash php2shell-docker.sh # add --images to include local images
Paste the output
Script output works best, but you can also paste php -v, php -i, phpinfo(), rpm -q php, dpkg -l 'php*' or curl -I headers. Include /etc/os-release if you have it.
Enter a site you own or are authorised to test
We make one ordinary GET request, the same as a browser visit. We read the response headers and up to 64 KB of the page, and we send no payloads, no uploads and no extra paths. The result is indicative only: servers can hide or misreport their version.
Paste many outputs, or load an inventory
Concatenate script outputs from as many servers as you like (each begins with [host]), or use a CSV with a php_version column. Optional columns: host, os_id, os_version, pkg_name, pkg_version, sapi. Everything stays in your browser.
.csv / .txt file here
PHP2Shell exposure report
Can't patch tonight? Interim mitigations
These reduce exposure while you schedule the update. None of them replaces it, and each has a cost. The strongest option is blocking multipart/form-data, because PHP parses those bodies before your application code runs.
Generate a config snippet
What each option buys you
- Block multipart/form-data at the edge or web server. This is the most effective interim control. It breaks file uploads and any form that posts multipart, including some checkout and contact forms.
- Set
session.upload_progress.enabled = Off. This closes the upload-progress path behind the CVE-2026-109618 memory disclosure. Advisories call it partial: it does not stop the other trigger paths. - Cap request size in the web server (
client_max_body_size,LimitRequestBody). The trigger relies on memory exhaustion, so smaller bodies raise the bar. Chunked requests without a Content-Length are reported to bypasspost_max_size, so set the limit in the web server, not only in PHP. - Turn off
fatal_error_backtraceson PHP 8.5. Exploitation of CVE-2026-109619 was confirmed with it enabled. - Accept GET and HEAD only, or take PHP offline, for sites that can tolerate it. Application maintenance modes don't help: the bug triggers before the application runs.
- Avoid
memory_limit = -1, despite some advisories suggesting it. It removes a trigger but lets a single request exhaust server memory.
Were you hit? Compromise-hunting checklist
Attacks were reported before the fix shipped on 21 Oct 2026. If a server was exposed from 20 Oct onwards, work through this list even after patching. Adjust paths to your stack.
Fixed versions and vendor status
The data behind every verdict. Statuses the vendor has not published show as unconfirmed, and the tool treats them as exposed. Last updated 22 Oct 2026. Raw JSON.
| Branch | Fixed in | Status |
|---|
| Distribution | Package | Fixed package | Status | Reference |
|---|
How verdicts work
- Upstream-versioned builds (php.net source, Windows, official Docker images, Ondřej Surý, Remi) are compared on the PHP version against 8.5.12, 8.4.27, 8.3.36 or 8.2.35. PHP 8.1 and older have no upstream fix.
- Distro builds (Ubuntu, Debian, RHEL and its rebuilds) are compared on the package version, using Debian or RPM ordering. On AlmaLinux, Rocky and Oracle Linux, the version and leading release number are compared against Red Hat's fix.
- Unconfirmed means the vendor has not published a fixed package for that release yet. It is never shown as safe.
- URL checks only see a version banner, if there is one, so their verdicts are always marked indicative.