Research & assessment tool for CVE-2026-109618 and CVE-2026-109619 ("PHP2Shell").
A single-file, stdlib-only Python tool that fingerprints a target's PHP version and CMS/platform, correlates the two CVEs with live threat intel (CISA KEV + FIRST EPSS), and produces a coarse exposure verdict — without shipping, or even attempting, any code-execution payload.
This tool is provided for authorized security research and assessment only.
- Run it only against systems you own or have explicit written authorization to test.
- It fingerprints and assesses exposure — it does not perform code execution, send exploit payloads, poison sessions, or write files on any target.
- The author assumes no liability for misuse or unauthorized testing.
Both CVEs are internal PHP memory-safety bugs:
| CVE | Class | Advisory |
|---|---|---|
| CVE-2026-109618 | Out-of-memory handling corrupts partially-initialized request data | GHSA-w8hw-w9fx-j79w |
| CVE-2026-109619 | Use-after-free: request state outlives freed memory, reused by a later request on the same worker | GHSA-6wmf-4rv5-p3rp |
Both are rated CVSS v4.0 9.2 (critical). There is no safe, reliable boolean "trigger" for them: a genuine attempt to drive the UAF/OOM condition tends to crash the worker and amounts to weaponization. So this tool determines exposure the responsible way — by fingerprinting the reported PHP version and configuration and comparing against the vendor's fixed releases.
- Vulnerable: PHP 5.4 through 8.5.11
- Fixed builds: 8.5.12 · 8.4.27 · 8.3.36 · 8.2.35
- EOL branches (≤ 8.1): vulnerable, no official fix — upgrade to a
supported branch; interim mitigation:
session.upload_progress.enabled=0+ restart all PHP workers (not a complete fix).
- ✅ A PHP version detector (from
Server/X-Powered-By, and — in active mode — PHP's version-disclosure "egg"), with a heuristic "PHP present but version hidden" signal. - ✅ A CMS/platform fingerprinter (10 built-ins + overridable via JSON).
- ✅ A version-based exposure assessor (
vulnerable/patched/unknown). - ✅ A threat-intel correlator — live CISA KEV presence + FIRST EPSS scores.
- ✅ A scanner: concurrent, proxy-aware, multiple output formats.
- ❌ A weaponized RCE exploit or PoC.
- ❌ A session-poisoning /
session.upload_progressprimitive prober. - ❌ A deserialization gadget chain, LFI sink scanner, or file writer.
- ❌ A guarantee of exploitability — a version match is exposure, not proof.
- Python 3.10+ (uses structural
matchandX | Nonetyping) - No third-party dependencies — standard library only.
python3 script_test.py https://target.exampleThe tool prints a banner + colored table to the terminal, and a one-line
threat-intel summary. Colors, banner, and the live progress indicator are
automatically disabled when output is piped or when --format json|csv.
positional arguments:
urls target URLs
options:
-h, --help show this help message and exit
-i, --input FILE file with one URL per line ('-' for stdin)
--no-safe-mode active recon: version-disclosure egg + CMS marker
paths (non-destructive)
--workers N concurrent workers (default 10)
--scheme {https,http,both} scheme to assume for bare hosts (default https)
--timeout SECONDS per-request timeout (default 15)
--retries N transport-error retries (default 2)
--insecure skip TLS certificate verification
--proxy URL proxy URL, e.g. http://127.0.0.1:8080 (Burp/mitmproxy)
-H, --header "NAME: VALUE" extra request header (repeatable)
--user-agent UA override the User-Agent
--signatures FILE external CMS signature JSON instead of built-ins
--no-intel skip live CISA KEV / EPSS correlation
--fail-on {never,vulnerable,unknown,error}
exit nonzero at this severity or worse (default vulnerable)
--format {table,json,csv,md}
--out FILE write output to a file
--log FILE also write timestamped logs to this file
--no-color disable ANSI colors
--no-banner suppress the startup banner
-v, --verbose
Neither mode performs code execution or sends an exploit payload. The difference is only how the PHP version and CMS are discovered.
| Mode | Flag | Behaviour |
|---|---|---|
| safe (default) | (none) | Passive. Reads the PHP version and CMS only from response headers and the landing-page HTML. One request per target (plus scheme fallback). |
| active | --no-safe-mode |
Adds non-destructive recon: PHP's version-disclosure "egg" query (recovers the version even when expose_php is off) and a few well-known CMS marker paths (e.g. /readme.html, /CHANGELOG.txt). Still read-only. |
| Status | Meaning |
|---|---|
vulnerable |
Detected PHP version is below the fixed build for its branch, or on an EOL branch. |
patched |
Detected version ≥ the fixed build for its branch. |
unknown |
Version could not be determined (often PHP present but banner hidden), or outside the tracked range. |
error |
Target unreachable (DNS/TLS/timeout after retries). |
Exit codes: 0 clean · 1 a result met --fail-on severity (default
vulnerable) · 2 usage error.
# Single target, table + live KEV/EPSS intel
python3 script_test.py https://example.com
# Concurrent scan from a file, active recon
python3 script_test.py --no-safe-mode --workers 20 -i urls.txt
# Through Burp, with a session cookie, machine-readable JSON
python3 script_test.py https://example.com \
--proxy http://127.0.0.1:8080 -H "Cookie: SESS=..." --format json
# Obsidian-ready Markdown report (YAML frontmatter + tags)
python3 script_test.py -i urls.txt --format md --out report.md
# CSV for spreadsheets; try HTTP as well as HTTPS; fail CI on 'unknown' too
python3 script_test.py -i urls.txt --format csv --scheme both --fail-on unknown- table — colored terminal table with an intel banner (default).
- json —
{ "results": [...], "intel": {...} }, one object per target with full evidence (evidence,final_url,checked_at,php_detected, …). - csv — flat rows for spreadsheets / data pipelines.
- md — Markdown with YAML frontmatter and
#threat-inteltags, ready to drop into an Obsidian vault.
On each run (unless --no-intel) the tool fetches:
- CISA KEV — whether CVE-2026-109618/109619 are in the Known Exploited
Vulnerabilities catalog yet (cached locally for 6h at
~/.cache/php2shell/). - FIRST EPSS — current exploitation-probability scores for both CVEs.
These appear as an Intel — banner (table/md) or an intel object (json).
Override the built-in fingerprints with --signatures file.json:
[
{
"name": "CustomCMS",
"html_patterns": ["X-Custom-Thing", "powered by customcms ?(\\d[\\d.]*)"],
"active_paths": ["/custom/version.txt"],
"version_pattern": "customcms ?(\\d[\\d.]*)"
}
]Built-in CMS fingerprints (10): WordPress, Joomla, Drupal, Magento, TYPO3,
PrestaShop, Laravel, phpBB, MediaWiki, Plesk — plus a generic "PHP present"
heuristic (via PHPSESSID cookie, X-Powered-By, or .php references).
python3 -m pytest -q # 30 tests
python3 test_script_test.py # same, if pytest isn't installedTests never touch the network — the HTTP layer is monkeypatched. They cover the
version-assessment boundaries, CMS detection, scheme fallback, --fail-on
logic, header parsing, and the full check_url pipeline.
.
├── README.md
├── CLAUDE.md # guidance for AI coding agents
├── script_test.py # main tool (single file, stdlib only)
└── test_script_test.py # pytest suite (30 tests)
- sudo3rs (Mas Riyan)
- GitHub: github.com/masriyan/php2shell
- LinkedIn: linkedin.com/in/sudo3rs