Checks that your Tenable scanning is set up to detect PHP2Shell (CVE-2026-109618, CVE-2026-109619) on every host where Tenable has seen PHP, and tells you which hosts need a rescan or a settings change.
Tenable detects PHP2Shell once a host is scanned with the PHP2Shell plugins and the right settings. Hosts that haven't had that scan yet show no finding, the same as patched hosts. This tool tells them apart. For every PHP host, it joins:
- the PHP installs Tenable detected (plugins 48243, 58987, 216936, and 200476), with versions
- every finding for the two CVEs, from any plugin, including distro and "unpatched" checks
- the host's latest Nessus Scan Information (plugin 19506), which records the plugin feed the scan used, whether it was credentialed, and settings such as "Assess component installs"
Each host gets a verdict and a reason: vulnerable, likely-vulnerable (an affected version that no PHP2Shell plugin reports), unverified (no finding yet, because the last scan wasn't set up to detect it), patched, or no-finding. It also prints the list of hosts to rescan.
It works with Tenable Vulnerability Management, Tenable Security Center, and .nessus
files from any Nessus edition. It needs no Tenable One or Hexa AI subscription. It runs as
a Claude Code skill or as a standalone script.
PHP2Shell coverage is evolving quickly. Tenable keeps publishing plugins and updating its guidance, and this tool will be updated to match. See Keeping up with PHP2Shell coverage.
PHP2Shell was disclosed, and its plugins published, on 2026-10-21. Any scan before then couldn't report it, and the plugins keep growing as distro advisories and product checks are added. In the first days and weeks of an emergency like this, how many findings you have depends as much on when and how each host was last scanned as on how many hosts are vulnerable.
- Turns "no finding" into something you can act on. Instead of a count of affected hosts, you get every PHP host sorted into vulnerable, needs a rescan, or done, each with the reason and the fix.
- Finds the scan settings that matter. It reads the plugin feed, credentials, "Assess component installs", and "Scan for Unpatched Vulnerabilities" from each host's last scan, so you see which hosts need a fresh, credentialed scan, and which scan policies to change.
- Flags end-of-life PHP. PHP 8.1 and older have no fixed release. The tool grades the detected version, so these hosts appear even when no CVE finding exists for them yet.
- Avoids false alarms on distro PHP. Distro packages, such as RHEL's PHP 8.0.30, carry backported fixes under old version numbers. The tool leaves those to the distro plugins rather than grading the version.
- Gives you the rescan list (
targets), one address per line, ready to paste into a scan. - Works where you already are. Vulnerability Management, Security Center, or plain
.nessusexports, with read-only API access. - Helps you answer leadership with confidence. "N hosts vulnerable, M patched, K rescans scheduled" is a better status than a finding count alone.
The CyberAgents Exchange already has two PHP2Shell listings. They answer where are we exposed, what do we fix first, and were we hit? This tool answers the question before those: are our scans set up to find it everywhere?
| Answers | Data source | |
|---|---|---|
| PHP2Shell Coverage Check (this) | Which PHP hosts have been scanned in a way that can detect PHP2Shell, and which need a rescan or settings change | Vulnerability Management or Security Center API, or .nessus files |
| PHP2Shell Tenable Skill | Which assets are exposed, graded against the fixed releases, as an HTML, PDF, and CSV report | Tenable One, through Hexa AI MCP |
| PHP2Shell Response Playbook | Find, fix, and hunt: exposure, a fix order, and a threat hunt in your SIEM and EDR | Tenable One through Hexa AI MCP, plus SIEM and EDR |
They work well together:
- Run this check first, and rescan the hosts it lists with a current feed and credentials.
- Then run the exposure report or the playbook, so it works from complete scan data.
- Run this check again if the report shows hosts as unknown, or as having no PHP2Shell finding, to see whether the scan or the host is the reason.
If you don't have Tenable One or Hexa AI, this tool works on its own, and its report includes the fixed versions and interim mitigations.
- Python 3.11 or later, and uv (recommended) or pip.
- One of:
- Tenable Vulnerability Management: an API key pair whose user has the Basic role (or
the vulnerability export privilege) and Can View on the assets you want covered. Set
TIO_ACCESS_KEYandTIO_SECRET_KEY, andTIO_URLif you don't usecloud.tenable.com. - Tenable Security Center: an API key pair for an organization user (administrators
can't see vulnerability data), and the URL. Set
TSC_ACCESS_KEY,TSC_SECRET_KEY, andTSC_URL. - Nessus: one or more
.nessusexports. Any edition can export them from its UI, so no API access is needed. Security Center scan results download as a.zipholding the.nessusfile; pass the zip as it is.
- Tenable Vulnerability Management: an API key pair whose user has the Basic role (or
the vulnerability export privilege) and Can View on the assets you want covered. Set
git clone https://github.com/lanz/php2shell-coverage-check ~/.claude/skills/php2shell-coverage-checkThen ask, for example, "Are our scans set up to detect PHP2Shell everywhere?" or "Which
hosts do we need to rescan for PHP2Shell?", or run /php2shell-coverage-check.
git clone https://github.com/lanz/php2shell-coverage-check
cd php2shell-coverage-check
uv run scripts/coverage_check.py --help # uv installs pyTenable from the script's metadatauv run scripts/coverage_check.py collect # Vulnerability Management
uv run scripts/coverage_check.py collect --platform sc # Security Center
uv run scripts/coverage_check.py collect --nessus a.nessus b.nessus
uv run scripts/coverage_check.py report # markdown summary and hosts to act on
uv run scripts/coverage_check.py report --format csv > php2shell.csv
uv run scripts/coverage_check.py report --verdict unverified likely-vulnerable
uv run scripts/coverage_check.py targets > rescan.txt # one address per lineUse --env-prefix PROD to read PROD_ACCESS_KEY, PROD_SECRET_KEY, and PROD_URL
instead, and --snapshot to choose where the snapshot goes.
The markdown report starts with the verdict counts and the reasons hosts are unverified:
| Verdict | Hosts | Meaning |
|---|---|---|
| **vulnerable** | 1 | A PHP2Shell plugin reports it |
| **likely-vulnerable** | 1 | Detected PHP version is affected, but no PHP2Shell plugin reports it |
| **unverified** | 2 | No finding yet: the last scan wasn't set up to detect it |
| patched | 0 | Fixed finding, or a fixed version seen by a capable scan |
| no-finding | 1 | A capable scan reported no finding |
- 2 × last scanned with plugin feed 202609110358, before the PHP2Shell plugins existed
It then lists each host to act on with its reason, action, PHP versions, last scan, plugin
feed, and whether it was credentialed. CSV and JSON include every host and a notes column.
references/verdicts.md explains every verdict and reason.
- Vulnerability Management: two vulnerability exports.
- The inventory plugins plus plugin 19506, open findings from the last 400 days
(
--since-dayschanges the window). - Every finding for the two CVEs (open, reopened, and fixed), filtered by CVE ID, so new distro plugins are included automatically.
- The inventory plugins plus plugin 19506, open findings from the last 400 days
(
- Security Center: the same data through cumulative and patched analysis queries.
.nessusfiles, or zips of them: the latest scan of each host wins, so pass older and newer exports together.
Verdicts are computed when you run report, so the snapshot doesn't need to be collected
again if the verdict rules change. The PHP2Shell specifics (CVEs, plugin IDs, fixed
versions, and the date the plugins were published) live in profiles/php2shell.json.
- Read-only. Every request reads data. On Vulnerability Management, collection starts two export jobs, which change nothing, and cancels them if it stops early. If an identical export is already running, the tool reads that one instead and leaves it to its owner. It contacts only the Tenable URL you configure, and sends no telemetry.
- TLS. Security Center certificates are verified. Pass
--ca-bundlefor a private CA;--insecureturns verification off and isn't recommended. - Credentials come from environment variables only and are never written anywhere.
- The snapshot (under
~/.cache/php2shell-coverage-check/, orPHP2SHELL_CHECK_HOME) lists host names, addresses, PHP versions, and scan settings. It's written with owner-only permissions. Treat it as internal and don't commit it. - With the skill, the assistant reads the report, so host names in it go to the model provider like anything else you share.
- Only hosts where Tenable detected PHP appear. Unauthenticated scans see PHP only when a web server reveals its version, and inventory plugins can miss some installs. Until plugin feed 202610271613, Unix local detection didn't recognize PHP-FPM, so a host that runs PHP only as PHP-FPM and was last scanned before then won't appear. If another tool shows PHP on a host this check doesn't list, rescan that host with credentials.
- Appliances that bundle PHP aren't listed, such as F5 BIG-IP or NetScaler ADC and Gateway, because the PHP inventory plugins don't detect them. Their own advisory plugins report PHP2Shell, and any finding they produce is included. F5's plugin (372182) reports only when the scan's report paranoia is set to Paranoid; the CSV and JSON reports show each host's setting.
- Version grading is a guide. Distro packages often carry backported fixes under an old version number. Those hosts aren't graded by version; distro plugins decide, and the report says so.
- The scan information is per host, not per install. If a host has several PHP installs, the verdict uses the most urgent one.
- Security Center results cover only the repositories the API user can see.
- Coverage keeps changing. See the next section.
PHP2Shell coverage is evolving quickly. Tenable Research is still publishing plugins (distro advisories, unpatched-package checks, and product-specific checks) and updating its guidance, including recommended scan settings. By 2026-11-06, about 124 plugins covered each CVE, all of them version-based; Tenable Research explains why it didn't release a direct check in php2shell one week later.
- New plugins for the two CVEs are picked up automatically. Findings are collected by CVE ID, not plugin ID.
- Everything else is in
profiles/php2shell.json: the inventory plugins, the date the plugins were published, the earliest plugin feed that can detect it, the fixed versions, and the interim mitigations. They reflect coverage and guidance as of 2026-11-06. - This tool will be updated as coverage evolves. Run
git pullin the skill folder before each use while PHP2Shell is active, and check the releases page for changes. - Tenable's guidance comes first. If it differs from this tool, follow the Tenable Research PHP2Shell FAQ, and please open an issue so the profile can be updated.
pip install pytenable pytest responses ruff
pytest
ruff check scripts testsThe tests use synthetic data in the formats of real plugin output, and mocked HTTP. They don't contact Tenable.
Licensed under the MIT License. Copyright (c) 2026 Lance Duvall.
This is an independent open source community project. It is not an official Tenable product and is not affiliated with, sponsored by, endorsed by, or supported by Tenable, Inc.
Tenable, Nessus, and other Tenable product names are trademarks or registered trademarks of Tenable, Inc. Other names may be trademarks of their respective owners.