- discovers versioned and unversioned PHP binaries across `PATH` and common locations;
- executes a small read-only PHP expression to collect the exact runtime version, SAPI,
loaded `php.ini`, and relevant temporary-mitigation settings;
- records Debian, RPM, or Alpine package provenance when available;
- compares PHP 8.2–8.5 against the official fixed-release boundaries;
- identifies PHP 5.4–8.1 as affected/EOL while warning possible vendor backports;
- keeps **affected version**, **patched**, **unknown**, and **operational error** distinct;
- passively reads `X-Powered-By` and `Server` headers with same-origin redirects only;
- scans target lists concurrently with bounded workers, timeout, and target-count limits;
- requires an explicit authorization acknowledgement before network traffic;
- hunts offline logs for upload-progress, multipart-session, OOM, worker-crash,
heap-corruption, and use-after-free indicators without printing raw log lines;
- optionally snapshots process lineage for shells and network tools directly spawned
by PHP-FPM, Apache, LiteSpeed, CGI, or FrankenPHP workers;
- supports a no-traffic dry run and offline single-version assessment;
- emits human-readable text, stable JSON, and GitHub-compatible SARIF 2.1.0;
- has no runtime dependencies beyond Python 3.10 or newer.
From a source checkout, `./phpscanner.py` is also available as a convenience launcher.
Discover and inspect local PHP installations:
Inspect explicit binaries and write JSON:
The configuration observations are not treated as fixes. PHP states that an
updated runtime is the only complete mitigation. After updating, restart or
redeploy every FPM, Apache, CGI, FrankenPHP, or LiteSpeed worker.
Preview a target list without sending traffic:
php2shell-audit remote --target-file targets.txt --dry-run
Send one bounded `HEAD` request to each authorized target:
--acknowledge-authorization I_HAVE_AUTHORIZATION
--format sarif --output php2shell.sarif \
--acknowledge-authorization I_HAVE_AUTHORIZATION
Remote output is intentionally low-confidence. Headers may be absent, forged,
cached, or inserted by a proxy, and vendors can backport fixes without changing
the upstream-looking version. An `UNKNOWN` result is not a clean bill of health.
php2shell-audit version 8.4.27 --format json
Inspect explicit logs or bounded directories without sending network traffic:
--format json --output php2shell-hunt.json
Compressed `.gz` logs are supported. The scanner caps the number of files and
bytes read, never emits raw log lines or process arguments, and records a
SHA-256 digest for every matching line. Findings are triage leads—not proof of
exploitation—and should be correlated with surrounding requests, worker logs,
container telemetry, and file changes. See
[docs/INCIDENT_RESPONSE.md](docs/INCIDENT_RESPONSE.md).
PHP 8.1 and older are end-of-life upstream. Some distribution or LTS vendors
may ship backports, which must be confirmed against that vendor's advisory.
The convention matches `jwt-audit`, `oidc-audit`, and `noirspect`:
| `0` | No affected version was detected and no operational error occurred. |
| `1` | Invalid input or an operational error prevented a complete check. |
| `2` | One or more affected versions were detected. |
`UNKNOWN` alone exits `0` because it is not a confirmed finding. Consumers
must still inspect the structured summary and should not interpret unknown as patched.
Tests use subprocess and HTTP fakes and never external targets.
This is a version and provenance scanner, not an exploit verifier. Even a local
version comparison cannot prove exploitability because downstream vendors and
private builds may backport patches. Conversely, failure to observe a remote
PHP header proves nothing. See [docs/METHODOLOGY.md](docs/METHODOLOGY.md).
Public exploit research was reviewed to improve defensive coverage, including
[ikow/php2shell-poc](
[eness/php2shell-CVE-2026-109618-CVE-2026-109619-PoC](
and [Zer061/CVE-2026-109618](
Their payload builders and active triggers are deliberately not included.
Sources: [PHP security announcement](
[CVE-2026-109618 advisory](
and [CVE-2026-109619 advisory](
Created and maintained by [Liam Giannini]( of
[Webcracy.ch]( Licensed under the MIT License.
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
