Skip to main content

phpscan — free open-source PHP vulnerability scanner

Is your PHP version vulnerable to php2shell? phpscan is a free, disclosure-only scanner that answers that question for any public website. It checks the PHP version a site discloses against the fixed releases for php2shell — CVE-2026-109618 / CVE-2026-109619 (CVSS v4 9.2, unauthenticated remote code execution in PHP core) — and, more generally, against the branch floors in its advisory database.

It is the open-source scanner behind phpatch.com.

License: MIT Python 3.11+ Dependencies: zero Tests

What it does

  • Disclosure-only. It reads what a site already discloses publicly — X-Powered-By / Server headers, PHPSESSID cookies, generator tags — plus a limited check for a publicly exposed phpinfo page at a few well-known paths. It never sends an exploit probe, a crafted payload, or anything designed to trigger the bug.
  • Honest verdicts. If a site hides its version, the scanner says so (VERSION HIDDEN) instead of guessing. If a build looks like a distribution package, a below-floor version is reported as a check signal (POSSIBLY VULNERABLE), because distros backport fixes without changing the version string.
  • Public targets only. The fetcher refuses loopback, private and link-local addresses (including cloud metadata endpoints), non-HTTP(S) schemes and ports other than 80/443, pins the validated IP, and re-validates every redirect — it is designed so it cannot be pointed at internal services. The refusals are exercised by the test suite in tests/ on every push.
  • Zero dependencies. Python 3.11+ standard library only.

Verdicts

Every scan returns one JSON object per URL. The verdict.status field uses the vocabulary below (shown here in its display form). Actionable verdicts (vulnerable, possibly_vulnerable, eol) also carry a remediation field naming the upgrade that fixes them; patched verdicts carry none.

Verdict JSON status Meaning
VULNERABLE vulnerable The disclosed version is below the fixed release for its branch, and a patched release exists.
POSSIBLY VULNERABLE possibly_vulnerable Below the floor, but the build looks like a distribution package — distros backport fixes, so this is a check signal, not proof.
PATCHED patched The disclosed version is at or above every advisory floor for its branch. (Not a general security guarantee — only "not below the fixed releases in the database.")
END OF LIFE eol The branch is below the advisory's EOL line (< 8.2 for php2shell): no patched release exists. The only complete remedies are a branch upgrade or taking the site offline.
VERSION HIDDEN indeterminate PHP is present but the version is not disclosed, so patch status cannot be confirmed from outside. Most well-run sites land here.
NOT ASSESSED not_assessed The branch is newer than the advisory database, so the scanner cannot judge it.

Two more statuses cover the scan itself: no_php_detected (no PHP signals found) and unreachable (the site did not answer, or the target was refused by the safety policy).

Sample session

Recorded against a local test fixture that discloses X-Powered-By: PHP/8.3.20 — the output format is exactly what the tool prints:

$ python3 -m phpscan http://fixture.example
{
 "target": "http://fixture.example",
 "reachable": true,
 "http_status": 200,
 "final_url": "http://fixture.example",
 "error": null,
 "signals": {
  "x_powered_by": "PHP/8.3.20",
  "server": "Apache/2.4.62"
 },
 "php_version": "8.3.20",
 "version_source": "X-Powered-By",
 "cms": [],
 "phpinfo_exposed": false,
 "verdict": {
  "status": "vulnerable",
  "version": "8.3.20",
  "branch": "8.3",
  "advisory": "php2shell-2026-10",
  "cves": [
   "CVE-2026-109618",
   "CVE-2026-109619"
  ],
  "cvss_v4": 9.2,
  "floor": "8.3.36",
  "detail": "PHP 8.3.20 is below the patched release 8.3.36 for branch 8.3 (CVE-2026-109618, CVE-2026-109619)."
 }
}
$ echo $?
1

Exit code 1 = an actionable finding. A site that runs PHP but hides its version returns the VERSION HIDDEN verdict instead (verdict block shown; the rest of the object lists the signals it did see):

"verdict": {
 "status": "indeterminate",
 "detail": "PHP is present (fingerprinted) but the version is not disclosed, so the patch status cannot be confirmed from outside. Run the deep check (SSH, read-only) for a definitive verdict."
}

Branch floors (php2shell)

A branch is PATCHED at or above its fixed release; anything below it is VULNERABLE (or POSSIBLY VULNERABLE for distro builds):

PHP branch Fixed in
8.2 8.2.35
8.3 8.3.36
8.4 8.4.27
8.5 8.5.12
< 8.2 No fix — END OF LIFE (branch upgrade or offline)

The floors live in phpscan/data/cves.json, reviewed against the php.net release announcements on every PHP security release (see Advisory data).

Install

git clone https://github.com/PHPatch/phpscan.git
cd phpscan
# Python 3.11+ standard library only — no pip install, no dependencies.

Or install it directly from GitHub, no clone needed:

pip install git+https://github.com/PHPatch/phpscan.git
python3 -m phpscan https://your-site.com

Run it

From the clone, run it right there — no install step:

python3 -m phpscan https://your-site.com

Prefer a zip? Download phpscan.zip from phpatch.com, then either install it and run from anywhere:

python3 -m pip install phpscan.zip
python3 -m phpscan https://your-site.com

or unzip and run in place:

unzip phpscan.zip
cd phpscan
python3 -m phpscan https://your-site.com

Output is one JSON object per URL: the verdict, the disclosed PHP version and where it was disclosed, and CMS fingerprints.

Exit codes (for scripts and CI): 0 — scanned, no actionable finding; 1 — at least one target is vulnerable, possibly_vulnerable, or on an end-of-life branch; 2 — usage error, or a target could not be judged at all: unreachable, or not_assessed (its PHP branch is newer than the advisory database, so the scanner cannot judge it). With several targets, the worst code wins.

Prefer a browser? The same scan runs free at phpatch.com and includes a PDF report.

How it compares

The php2shell scanners that appeared in October 2026 differ mostly in how they decide you are vulnerable — and in whether they try to trigger the bug to find out:

Tool Detection Sends exploit triggers? Remediation License
phpscan (this repo) Version fingerprint from what the site discloses; says "version hidden" instead of guessing No — never Via PHP Patch Assurance: snapshot-first patch, verify, rollback, evidence report MIT
OpenAI php2shell-checker Version fingerprint from X-Powered-By / Server headers only No None Apache-2.0
Searchlight Cyber php2shell-scanner Behavioral — crafted multipart probes force PHP out of memory and watch for the crash Yes, deliberately None No LICENSE file
SamJUK php2shell-check Behavioral, local — observes how the installed PHP binary terminates at its memory limit Only against a throwaway local CLI process None MIT
squiblyza php_vuln_scan Version fingerprint from headers/markup, plus version-disclosure hunting No None MIT
JoasASantos php2shell-scanner Version fingerprint + subdomain recon + origin scan Optional opt-in probe (README warns of DoS risk) None README states MIT; no LICENSE file

Full criteria, the fairness notes (behavioral checks catch backports; crash signals also misfire on some patched builds), SSRF-safety comparison and an FAQ: PHP vulnerability scanners compared.

What it doesn't do

  • It doesn't patch anything. The scanner is read-only and external; it cannot change your server.
  • It can't see a hidden version. From outside, VERSION HIDDEN is the honest end of the road — a definitive verdict for those sites needs a read-only look on the server itself.
  • It doesn't monitor or alert. It's a point-in-time check you run; it won't notice next month's advisory for you.
  • It isn't a general security audit. It judges the disclosed PHP version against its advisory database — nothing more.

Patching, on-server verification, rollback, an evidence work report, and monitoring are what the hosted service adds: PHP Patch Assurance — phpatch.com.

Guides and further reading

  • Self-hoster guides — update PHP in Docker and with apt, mod_php vs PHP-FPM, rollback, health checks, version checks, end-of-life upgrades, and patching without downtime, written from the patch flow the service itself uses.
  • php2shell fix guide — what php2shell is, who is affected, how to check and patch, the fixed releases, and the primary sources.
  • Case study: mitbbs.ai — how a bilingual news portal patched php2shell on disclosure night: 8.3.35 to 8.3.36 under mod_php, about 10 seconds of downtime, verified live, no compromise found.

Authorized use

Only scan sites you own or have permission to scan. The hosted scanner at phpatch.com additionally enforces rate limits and an opt-out blocklist for site operators (see the Terms, section 8).

Advisory data

phpscan/data/cves.json is the advisory database the scanner judges against — branch floors, fixed releases, references and the leak/exploitation timeline. Last updated 2026-10-22. Update process: floors are reviewed against the php.net release announcements on every PHP security release and at least monthly. Corrections are welcome as pull requests; every change to this file requires maintainer review (see CODEOWNERS), because it changes the verdicts the scanner gives.

A verdict of patched means only that the disclosed version is not below the fixed releases in this database — it is not a general security guarantee. A version below a floor on a distribution build (Ubuntu/Debian packages) is a "check" signal, not proof: distros backport fixes without changing the upstream version, and the scanner says so when it detects a distro build.

Support the project

phpscan is free and MIT-licensed, and the hosted scanner at phpatch.com is free for every site. If the scanner, the threat desk or the daily Mega-Vuln Alert reports helped you, you can chip in at phpatch.com/donate — any amount from $1, one-time, via Stripe. Donations fund phpscan development, the free hosted scans and the public threat research. Plainly stated on the page: PHPatch is operated by Superstar Telecom LLC, a for-profit company, so donations are voluntary support — not tax-deductible, and they buy no product, service or entitlement.

Scanned by phpscan badge

If you scan your site with phpscan, you can show it:

Scanned by phpscan

[![Scanned by phpscan](https://img.shields.io/badge/scanned%20by-phpscan-blue.svg)](https://phpatch.com)

License

MIT. Not affiliated with the PHP Group. PHP is a trademark of the PHP Group.

Metadata

Release files for phpscan 1.4.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for phpscan 1.4.4
File Size Uploaded Provenance Actions
phpscan-1.4.4.tar.gz 27.2 kB With provenance Details for phpscan-1.4.4.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for phpscan 1.4.4
File Interpreter ABI Platform Actions
phpscan-1.4.4-py3-none-any.whl Python 3 none any Details for phpscan-1.4.4-py3-none-any.whl

Total release size: 46.4 kB

Release files / phpscan-1.4.4.tar.gz

Download URL phpscan-1.4.4.tar.gz
Size 27.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7783289f8816b38330064d5e3d60d51d6201df1863be0f2a04ac8c14633230fd
BLAKE2b-256 checksum
How to use checksums
8bd0376c1d013244b3c54de1c65b57a4af2737740130dd5485cce35b14947c3c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.

Transparency log

Release files / phpscan-1.4.4-py3-none-any.whl

Download URL phpscan-1.4.4-py3-none-any.whl
Size 19.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
079d7c523fa3d3378214178bd3364df5c8f1c6caca3aacbe290c6bf8d6c79f4b
BLAKE2b-256 checksum
How to use checksums
9eee7ef536a88f4cca30879f09a776cee8ff81abeb327796ef86d46911cc161a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.

Transparency log

Security for phpscan 1.4.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for phpscan-1.4.4.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page