phpscan 1.4.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- phpscan-1.4.4/LICENSE +23 -0
- phpscan-1.4.4/PKG-INFO +294 -0
- phpscan-1.4.4/README.md +283 -0
- phpscan-1.4.4/phpscan/__init__.py +15 -0
- phpscan-1.4.4/phpscan/__main__.py +52 -0
- phpscan-1.4.4/phpscan/data/cves.json +1 -0
- phpscan-1.4.4/phpscan/scan.py +467 -0
- phpscan-1.4.4/phpscan/versioning.py +152 -0
- phpscan-1.4.4/phpscan.egg-info/PKG-INFO +294 -0
- phpscan-1.4.4/phpscan.egg-info/SOURCES.txt +14 -0
- phpscan-1.4.4/phpscan.egg-info/dependency_links.txt +1 -0
- phpscan-1.4.4/phpscan.egg-info/entry_points.txt +2 -0
- phpscan-1.4.4/phpscan.egg-info/top_level.txt +1 -0
- phpscan-1.4.4/pyproject.toml +21 -0
- phpscan-1.4.4/setup.cfg +4 -0
- phpscan-1.4.4/tests/test_scan_guard.py +637 -0
phpscan-1.4.4/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Superstar Telecom LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
20
|
+
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
21
|
+
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
22
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
phpscan-1.4.4/PKG-INFO
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phpscan
|
|
3
|
+
Version: 1.4.4
|
|
4
|
+
Summary: Free external PHP vulnerability scanner (php2shell / CVE-2026-109618, CVE-2026-109619) — passive fingerprinting plus limited, read-only path checks. No exploit probes.
|
|
5
|
+
Author: Superstar Telecom LLC
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# phpscan — free open-source PHP vulnerability scanner
|
|
13
|
+
|
|
14
|
+
**Is your PHP version vulnerable to php2shell?** phpscan is a
|
|
15
|
+
free, disclosure-only scanner that answers that question for any
|
|
16
|
+
public website. It checks the PHP version a site discloses against
|
|
17
|
+
the fixed releases for **php2shell — CVE-2026-109618 /
|
|
18
|
+
CVE-2026-109619** (CVSS v4 9.2, unauthenticated remote code
|
|
19
|
+
execution in PHP core) — and, more generally, against the branch
|
|
20
|
+
floors in its advisory database.
|
|
21
|
+
|
|
22
|
+
It is the open-source scanner behind [phpatch.com](https://phpatch.com).
|
|
23
|
+
|
|
24
|
+
[](LICENSE)
|
|
25
|
+
[](https://www.python.org/)
|
|
26
|
+
[](#what-it-does)
|
|
27
|
+
[](https://github.com/PHPatch/phpscan/actions/workflows/tests.yml)
|
|
28
|
+
|
|
29
|
+
## What it does
|
|
30
|
+
|
|
31
|
+
- **Disclosure-only.** It reads what a site already discloses
|
|
32
|
+
publicly — `X-Powered-By` / `Server` headers, `PHPSESSID` cookies,
|
|
33
|
+
generator tags — plus a limited check for a publicly exposed
|
|
34
|
+
phpinfo page at a few well-known paths. It never sends an exploit
|
|
35
|
+
probe, a crafted payload, or anything designed to trigger the bug.
|
|
36
|
+
- **Honest verdicts.** If a site hides its version, the scanner
|
|
37
|
+
says so (VERSION HIDDEN) instead of guessing. If a build looks
|
|
38
|
+
like a distribution package, a below-floor version is reported
|
|
39
|
+
as a check signal (POSSIBLY VULNERABLE), because distros backport
|
|
40
|
+
fixes without changing the version string.
|
|
41
|
+
- **Public targets only.** The fetcher refuses loopback, private
|
|
42
|
+
and link-local addresses (including cloud metadata endpoints),
|
|
43
|
+
non-HTTP(S) schemes and ports other than 80/443, pins the
|
|
44
|
+
validated IP, and re-validates every redirect — it is designed
|
|
45
|
+
so it cannot be pointed at internal services. The refusals are
|
|
46
|
+
exercised by the test suite in `tests/` on every push.
|
|
47
|
+
- **Zero dependencies.** Python 3.11+ standard library only.
|
|
48
|
+
|
|
49
|
+
## Verdicts
|
|
50
|
+
|
|
51
|
+
Every scan returns one JSON object per URL. The `verdict.status`
|
|
52
|
+
field uses the vocabulary below (shown here in its display form).
|
|
53
|
+
Actionable verdicts (`vulnerable`, `possibly_vulnerable`, `eol`)
|
|
54
|
+
also carry a `remediation` field naming the upgrade that fixes
|
|
55
|
+
them; patched verdicts carry none.
|
|
56
|
+
|
|
57
|
+
| Verdict | JSON `status` | Meaning |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| **VULNERABLE** | `vulnerable` | The disclosed version is below the fixed release for its branch, and a patched release exists. |
|
|
60
|
+
| **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. |
|
|
61
|
+
| **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.") |
|
|
62
|
+
| **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. |
|
|
63
|
+
| **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. |
|
|
64
|
+
| **NOT ASSESSED** | `not_assessed` | The branch is newer than the advisory database, so the scanner cannot judge it. |
|
|
65
|
+
|
|
66
|
+
Two more statuses cover the scan itself: `no_php_detected` (no PHP
|
|
67
|
+
signals found) and `unreachable` (the site did not answer, or the
|
|
68
|
+
target was refused by the safety policy).
|
|
69
|
+
|
|
70
|
+
## Sample session
|
|
71
|
+
|
|
72
|
+
Recorded against a local test fixture that discloses
|
|
73
|
+
`X-Powered-By: PHP/8.3.20` — the output format is exactly what the
|
|
74
|
+
tool prints:
|
|
75
|
+
|
|
76
|
+
```console
|
|
77
|
+
$ python3 -m phpscan http://fixture.example
|
|
78
|
+
{
|
|
79
|
+
"target": "http://fixture.example",
|
|
80
|
+
"reachable": true,
|
|
81
|
+
"http_status": 200,
|
|
82
|
+
"final_url": "http://fixture.example",
|
|
83
|
+
"error": null,
|
|
84
|
+
"signals": {
|
|
85
|
+
"x_powered_by": "PHP/8.3.20",
|
|
86
|
+
"server": "Apache/2.4.62"
|
|
87
|
+
},
|
|
88
|
+
"php_version": "8.3.20",
|
|
89
|
+
"version_source": "X-Powered-By",
|
|
90
|
+
"cms": [],
|
|
91
|
+
"phpinfo_exposed": false,
|
|
92
|
+
"verdict": {
|
|
93
|
+
"status": "vulnerable",
|
|
94
|
+
"version": "8.3.20",
|
|
95
|
+
"branch": "8.3",
|
|
96
|
+
"advisory": "php2shell-2026-10",
|
|
97
|
+
"cves": [
|
|
98
|
+
"CVE-2026-109618",
|
|
99
|
+
"CVE-2026-109619"
|
|
100
|
+
],
|
|
101
|
+
"cvss_v4": 9.2,
|
|
102
|
+
"floor": "8.3.36",
|
|
103
|
+
"detail": "PHP 8.3.20 is below the patched release 8.3.36 for branch 8.3 (CVE-2026-109618, CVE-2026-109619)."
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
$ echo $?
|
|
107
|
+
1
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Exit code 1 = an actionable finding. A site that runs PHP but
|
|
111
|
+
hides its version returns the VERSION HIDDEN verdict instead
|
|
112
|
+
(verdict block shown; the rest of the object lists the signals it
|
|
113
|
+
did see):
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
"verdict": {
|
|
117
|
+
"status": "indeterminate",
|
|
118
|
+
"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."
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Branch floors (php2shell)
|
|
123
|
+
|
|
124
|
+
A branch is PATCHED at or above its fixed release; anything below
|
|
125
|
+
it is VULNERABLE (or POSSIBLY VULNERABLE for distro builds):
|
|
126
|
+
|
|
127
|
+
| PHP branch | Fixed in |
|
|
128
|
+
|---|---|
|
|
129
|
+
| 8.2 | **8.2.35** |
|
|
130
|
+
| 8.3 | **8.3.36** |
|
|
131
|
+
| 8.4 | **8.4.27** |
|
|
132
|
+
| 8.5 | **8.5.12** |
|
|
133
|
+
| < 8.2 | No fix — **END OF LIFE** (branch upgrade or offline) |
|
|
134
|
+
|
|
135
|
+
The floors live in `phpscan/data/cves.json`, reviewed against
|
|
136
|
+
the php.net release announcements on every PHP security release
|
|
137
|
+
(see [Advisory data](#advisory-data)).
|
|
138
|
+
|
|
139
|
+
## Install
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
git clone https://github.com/PHPatch/phpscan.git
|
|
143
|
+
cd phpscan
|
|
144
|
+
# Python 3.11+ standard library only — no pip install, no dependencies.
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Or install it directly from GitHub, no clone needed:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
pip install git+https://github.com/PHPatch/phpscan.git
|
|
151
|
+
python3 -m phpscan https://your-site.com
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Run it
|
|
155
|
+
|
|
156
|
+
From the clone, run it right there — no install step:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
python3 -m phpscan https://your-site.com
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Prefer a zip? Download `phpscan.zip` from phpatch.com, then
|
|
163
|
+
either install it and run from anywhere:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
python3 -m pip install phpscan.zip
|
|
167
|
+
python3 -m phpscan https://your-site.com
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
or unzip and run in place:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
unzip phpscan.zip
|
|
174
|
+
cd phpscan
|
|
175
|
+
python3 -m phpscan https://your-site.com
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Output is one JSON object per URL: the verdict, the disclosed PHP
|
|
179
|
+
version and where it was disclosed, and CMS fingerprints.
|
|
180
|
+
|
|
181
|
+
Exit codes (for scripts and CI): **0** — scanned, no actionable
|
|
182
|
+
finding; **1** — at least one target is `vulnerable`,
|
|
183
|
+
`possibly_vulnerable`, or on an end-of-life branch; **2** — usage
|
|
184
|
+
error, or a target could not be judged at all: unreachable, or
|
|
185
|
+
`not_assessed` (its PHP branch is newer than the advisory
|
|
186
|
+
database, so the scanner cannot judge it). With several targets,
|
|
187
|
+
the worst code wins.
|
|
188
|
+
|
|
189
|
+
Prefer a browser? The same scan runs free at
|
|
190
|
+
[phpatch.com](https://phpatch.com) and includes a PDF report.
|
|
191
|
+
|
|
192
|
+
## How it compares
|
|
193
|
+
|
|
194
|
+
The php2shell scanners that appeared in October 2026 differ
|
|
195
|
+
mostly in **how they decide you are vulnerable** — and in whether
|
|
196
|
+
they try to trigger the bug to find out:
|
|
197
|
+
|
|
198
|
+
| Tool | Detection | Sends exploit triggers? | Remediation | License |
|
|
199
|
+
|---|---|---|---|---|
|
|
200
|
+
| **phpscan** (this repo) | Version fingerprint from what the site discloses; says "version hidden" instead of guessing | **No — never** | Via [PHP Patch Assurance](https://phpatch.com): snapshot-first patch, verify, rollback, evidence report | MIT |
|
|
201
|
+
| OpenAI php2shell-checker | Version fingerprint from `X-Powered-By` / `Server` headers only | No | None | Apache-2.0 |
|
|
202
|
+
| Searchlight Cyber php2shell-scanner | Behavioral — crafted multipart probes force PHP out of memory and watch for the crash | **Yes, deliberately** | None | No LICENSE file |
|
|
203
|
+
| 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 |
|
|
204
|
+
| squiblyza php_vuln_scan | Version fingerprint from headers/markup, plus version-disclosure hunting | No | None | MIT |
|
|
205
|
+
| 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 |
|
|
206
|
+
|
|
207
|
+
Full criteria, the fairness notes (behavioral checks catch
|
|
208
|
+
backports; crash signals also misfire on some patched builds),
|
|
209
|
+
SSRF-safety comparison and an FAQ:
|
|
210
|
+
**[PHP vulnerability scanners compared](https://phpatch.com/compare)**.
|
|
211
|
+
|
|
212
|
+
## What it doesn't do
|
|
213
|
+
|
|
214
|
+
- **It doesn't patch anything.** The scanner is read-only and
|
|
215
|
+
external; it cannot change your server.
|
|
216
|
+
- **It can't see a hidden version.** From outside, VERSION HIDDEN
|
|
217
|
+
is the honest end of the road — a definitive verdict for those
|
|
218
|
+
sites needs a read-only look on the server itself.
|
|
219
|
+
- **It doesn't monitor or alert.** It's a point-in-time check you
|
|
220
|
+
run; it won't notice next month's advisory for you.
|
|
221
|
+
- **It isn't a general security audit.** It judges the disclosed
|
|
222
|
+
PHP version against its advisory database — nothing more.
|
|
223
|
+
|
|
224
|
+
Patching, on-server verification, rollback, an evidence work
|
|
225
|
+
report, and monitoring are what the hosted service adds:
|
|
226
|
+
**[PHP Patch Assurance — phpatch.com](https://phpatch.com)**.
|
|
227
|
+
|
|
228
|
+
## Guides and further reading
|
|
229
|
+
|
|
230
|
+
- **[Self-hoster guides](https://phpatch.com/guides)** — update
|
|
231
|
+
PHP in Docker and with apt, mod_php vs PHP-FPM, rollback,
|
|
232
|
+
health checks, version checks, end-of-life upgrades, and
|
|
233
|
+
patching without downtime, written from the patch flow the
|
|
234
|
+
service itself uses.
|
|
235
|
+
- **[php2shell fix guide](https://phpatch.com/threats/php2shell)**
|
|
236
|
+
— what php2shell is, who is affected, how to check and patch,
|
|
237
|
+
the fixed releases, and the primary sources.
|
|
238
|
+
- **[Case study: mitbbs.ai](https://phpatch.com/case-studies/mitbbs-ai)**
|
|
239
|
+
— how a bilingual news portal patched php2shell on disclosure
|
|
240
|
+
night: 8.3.35 to 8.3.36 under mod_php, about 10 seconds of
|
|
241
|
+
downtime, verified live, no compromise found.
|
|
242
|
+
|
|
243
|
+
## Authorized use
|
|
244
|
+
|
|
245
|
+
Only scan sites you own or have permission to scan. The hosted
|
|
246
|
+
scanner at phpatch.com additionally enforces rate limits and an
|
|
247
|
+
opt-out blocklist for site operators (see the Terms, section 8).
|
|
248
|
+
|
|
249
|
+
## Advisory data
|
|
250
|
+
|
|
251
|
+
`phpscan/data/cves.json` is the advisory database the scanner
|
|
252
|
+
judges against — branch floors, fixed releases, references and the
|
|
253
|
+
leak/exploitation timeline. Last updated 2026-10-22. Update
|
|
254
|
+
process: floors are reviewed against the php.net release
|
|
255
|
+
announcements on every PHP security release and at least monthly.
|
|
256
|
+
Corrections are welcome as pull requests; every change to this
|
|
257
|
+
file requires maintainer review (see CODEOWNERS), because it
|
|
258
|
+
changes the verdicts the scanner gives.
|
|
259
|
+
|
|
260
|
+
A verdict of `patched` means only that the disclosed version is
|
|
261
|
+
not below the fixed releases in this database — it is not a
|
|
262
|
+
general security guarantee. A version below a floor on a
|
|
263
|
+
distribution build (Ubuntu/Debian packages) is a "check" signal,
|
|
264
|
+
not proof: distros backport fixes without changing the upstream
|
|
265
|
+
version, and the scanner says so when it detects a distro build.
|
|
266
|
+
|
|
267
|
+
## Support the project
|
|
268
|
+
|
|
269
|
+
phpscan is free and MIT-licensed, and the hosted scanner at
|
|
270
|
+
[phpatch.com](https://phpatch.com) is free for every site. If
|
|
271
|
+
the scanner, the threat desk or the daily Mega-Vuln Alert
|
|
272
|
+
reports helped you, you can chip in at
|
|
273
|
+
**[phpatch.com/donate](https://phpatch.com/donate)** — any
|
|
274
|
+
amount from $1, one-time, via Stripe. Donations fund phpscan
|
|
275
|
+
development, the free hosted scans and the public threat
|
|
276
|
+
research. Plainly stated on the page: PHPatch is operated by
|
|
277
|
+
Superstar Telecom LLC, a for-profit company, so donations are
|
|
278
|
+
voluntary support — not tax-deductible, and they buy no
|
|
279
|
+
product, service or entitlement.
|
|
280
|
+
|
|
281
|
+
## Scanned by phpscan badge
|
|
282
|
+
|
|
283
|
+
If you scan your site with phpscan, you can show it:
|
|
284
|
+
|
|
285
|
+
[](https://phpatch.com)
|
|
286
|
+
|
|
287
|
+
```markdown
|
|
288
|
+
[](https://phpatch.com)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## License
|
|
292
|
+
|
|
293
|
+
MIT. Not affiliated with the PHP Group. PHP is a trademark of the
|
|
294
|
+
PHP Group.
|
phpscan-1.4.4/README.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# phpscan — free open-source PHP vulnerability scanner
|
|
2
|
+
|
|
3
|
+
**Is your PHP version vulnerable to php2shell?** phpscan is a
|
|
4
|
+
free, disclosure-only scanner that answers that question for any
|
|
5
|
+
public website. It checks the PHP version a site discloses against
|
|
6
|
+
the fixed releases for **php2shell — CVE-2026-109618 /
|
|
7
|
+
CVE-2026-109619** (CVSS v4 9.2, unauthenticated remote code
|
|
8
|
+
execution in PHP core) — and, more generally, against the branch
|
|
9
|
+
floors in its advisory database.
|
|
10
|
+
|
|
11
|
+
It is the open-source scanner behind [phpatch.com](https://phpatch.com).
|
|
12
|
+
|
|
13
|
+
[](LICENSE)
|
|
14
|
+
[](https://www.python.org/)
|
|
15
|
+
[](#what-it-does)
|
|
16
|
+
[](https://github.com/PHPatch/phpscan/actions/workflows/tests.yml)
|
|
17
|
+
|
|
18
|
+
## What it does
|
|
19
|
+
|
|
20
|
+
- **Disclosure-only.** It reads what a site already discloses
|
|
21
|
+
publicly — `X-Powered-By` / `Server` headers, `PHPSESSID` cookies,
|
|
22
|
+
generator tags — plus a limited check for a publicly exposed
|
|
23
|
+
phpinfo page at a few well-known paths. It never sends an exploit
|
|
24
|
+
probe, a crafted payload, or anything designed to trigger the bug.
|
|
25
|
+
- **Honest verdicts.** If a site hides its version, the scanner
|
|
26
|
+
says so (VERSION HIDDEN) instead of guessing. If a build looks
|
|
27
|
+
like a distribution package, a below-floor version is reported
|
|
28
|
+
as a check signal (POSSIBLY VULNERABLE), because distros backport
|
|
29
|
+
fixes without changing the version string.
|
|
30
|
+
- **Public targets only.** The fetcher refuses loopback, private
|
|
31
|
+
and link-local addresses (including cloud metadata endpoints),
|
|
32
|
+
non-HTTP(S) schemes and ports other than 80/443, pins the
|
|
33
|
+
validated IP, and re-validates every redirect — it is designed
|
|
34
|
+
so it cannot be pointed at internal services. The refusals are
|
|
35
|
+
exercised by the test suite in `tests/` on every push.
|
|
36
|
+
- **Zero dependencies.** Python 3.11+ standard library only.
|
|
37
|
+
|
|
38
|
+
## Verdicts
|
|
39
|
+
|
|
40
|
+
Every scan returns one JSON object per URL. The `verdict.status`
|
|
41
|
+
field uses the vocabulary below (shown here in its display form).
|
|
42
|
+
Actionable verdicts (`vulnerable`, `possibly_vulnerable`, `eol`)
|
|
43
|
+
also carry a `remediation` field naming the upgrade that fixes
|
|
44
|
+
them; patched verdicts carry none.
|
|
45
|
+
|
|
46
|
+
| Verdict | JSON `status` | Meaning |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| **VULNERABLE** | `vulnerable` | The disclosed version is below the fixed release for its branch, and a patched release exists. |
|
|
49
|
+
| **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. |
|
|
50
|
+
| **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.") |
|
|
51
|
+
| **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. |
|
|
52
|
+
| **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. |
|
|
53
|
+
| **NOT ASSESSED** | `not_assessed` | The branch is newer than the advisory database, so the scanner cannot judge it. |
|
|
54
|
+
|
|
55
|
+
Two more statuses cover the scan itself: `no_php_detected` (no PHP
|
|
56
|
+
signals found) and `unreachable` (the site did not answer, or the
|
|
57
|
+
target was refused by the safety policy).
|
|
58
|
+
|
|
59
|
+
## Sample session
|
|
60
|
+
|
|
61
|
+
Recorded against a local test fixture that discloses
|
|
62
|
+
`X-Powered-By: PHP/8.3.20` — the output format is exactly what the
|
|
63
|
+
tool prints:
|
|
64
|
+
|
|
65
|
+
```console
|
|
66
|
+
$ python3 -m phpscan http://fixture.example
|
|
67
|
+
{
|
|
68
|
+
"target": "http://fixture.example",
|
|
69
|
+
"reachable": true,
|
|
70
|
+
"http_status": 200,
|
|
71
|
+
"final_url": "http://fixture.example",
|
|
72
|
+
"error": null,
|
|
73
|
+
"signals": {
|
|
74
|
+
"x_powered_by": "PHP/8.3.20",
|
|
75
|
+
"server": "Apache/2.4.62"
|
|
76
|
+
},
|
|
77
|
+
"php_version": "8.3.20",
|
|
78
|
+
"version_source": "X-Powered-By",
|
|
79
|
+
"cms": [],
|
|
80
|
+
"phpinfo_exposed": false,
|
|
81
|
+
"verdict": {
|
|
82
|
+
"status": "vulnerable",
|
|
83
|
+
"version": "8.3.20",
|
|
84
|
+
"branch": "8.3",
|
|
85
|
+
"advisory": "php2shell-2026-10",
|
|
86
|
+
"cves": [
|
|
87
|
+
"CVE-2026-109618",
|
|
88
|
+
"CVE-2026-109619"
|
|
89
|
+
],
|
|
90
|
+
"cvss_v4": 9.2,
|
|
91
|
+
"floor": "8.3.36",
|
|
92
|
+
"detail": "PHP 8.3.20 is below the patched release 8.3.36 for branch 8.3 (CVE-2026-109618, CVE-2026-109619)."
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
$ echo $?
|
|
96
|
+
1
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Exit code 1 = an actionable finding. A site that runs PHP but
|
|
100
|
+
hides its version returns the VERSION HIDDEN verdict instead
|
|
101
|
+
(verdict block shown; the rest of the object lists the signals it
|
|
102
|
+
did see):
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
"verdict": {
|
|
106
|
+
"status": "indeterminate",
|
|
107
|
+
"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."
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Branch floors (php2shell)
|
|
112
|
+
|
|
113
|
+
A branch is PATCHED at or above its fixed release; anything below
|
|
114
|
+
it is VULNERABLE (or POSSIBLY VULNERABLE for distro builds):
|
|
115
|
+
|
|
116
|
+
| PHP branch | Fixed in |
|
|
117
|
+
|---|---|
|
|
118
|
+
| 8.2 | **8.2.35** |
|
|
119
|
+
| 8.3 | **8.3.36** |
|
|
120
|
+
| 8.4 | **8.4.27** |
|
|
121
|
+
| 8.5 | **8.5.12** |
|
|
122
|
+
| < 8.2 | No fix — **END OF LIFE** (branch upgrade or offline) |
|
|
123
|
+
|
|
124
|
+
The floors live in `phpscan/data/cves.json`, reviewed against
|
|
125
|
+
the php.net release announcements on every PHP security release
|
|
126
|
+
(see [Advisory data](#advisory-data)).
|
|
127
|
+
|
|
128
|
+
## Install
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
git clone https://github.com/PHPatch/phpscan.git
|
|
132
|
+
cd phpscan
|
|
133
|
+
# Python 3.11+ standard library only — no pip install, no dependencies.
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Or install it directly from GitHub, no clone needed:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
pip install git+https://github.com/PHPatch/phpscan.git
|
|
140
|
+
python3 -m phpscan https://your-site.com
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Run it
|
|
144
|
+
|
|
145
|
+
From the clone, run it right there — no install step:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
python3 -m phpscan https://your-site.com
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Prefer a zip? Download `phpscan.zip` from phpatch.com, then
|
|
152
|
+
either install it and run from anywhere:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
python3 -m pip install phpscan.zip
|
|
156
|
+
python3 -m phpscan https://your-site.com
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
or unzip and run in place:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
unzip phpscan.zip
|
|
163
|
+
cd phpscan
|
|
164
|
+
python3 -m phpscan https://your-site.com
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Output is one JSON object per URL: the verdict, the disclosed PHP
|
|
168
|
+
version and where it was disclosed, and CMS fingerprints.
|
|
169
|
+
|
|
170
|
+
Exit codes (for scripts and CI): **0** — scanned, no actionable
|
|
171
|
+
finding; **1** — at least one target is `vulnerable`,
|
|
172
|
+
`possibly_vulnerable`, or on an end-of-life branch; **2** — usage
|
|
173
|
+
error, or a target could not be judged at all: unreachable, or
|
|
174
|
+
`not_assessed` (its PHP branch is newer than the advisory
|
|
175
|
+
database, so the scanner cannot judge it). With several targets,
|
|
176
|
+
the worst code wins.
|
|
177
|
+
|
|
178
|
+
Prefer a browser? The same scan runs free at
|
|
179
|
+
[phpatch.com](https://phpatch.com) and includes a PDF report.
|
|
180
|
+
|
|
181
|
+
## How it compares
|
|
182
|
+
|
|
183
|
+
The php2shell scanners that appeared in October 2026 differ
|
|
184
|
+
mostly in **how they decide you are vulnerable** — and in whether
|
|
185
|
+
they try to trigger the bug to find out:
|
|
186
|
+
|
|
187
|
+
| Tool | Detection | Sends exploit triggers? | Remediation | License |
|
|
188
|
+
|---|---|---|---|---|
|
|
189
|
+
| **phpscan** (this repo) | Version fingerprint from what the site discloses; says "version hidden" instead of guessing | **No — never** | Via [PHP Patch Assurance](https://phpatch.com): snapshot-first patch, verify, rollback, evidence report | MIT |
|
|
190
|
+
| OpenAI php2shell-checker | Version fingerprint from `X-Powered-By` / `Server` headers only | No | None | Apache-2.0 |
|
|
191
|
+
| Searchlight Cyber php2shell-scanner | Behavioral — crafted multipart probes force PHP out of memory and watch for the crash | **Yes, deliberately** | None | No LICENSE file |
|
|
192
|
+
| 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 |
|
|
193
|
+
| squiblyza php_vuln_scan | Version fingerprint from headers/markup, plus version-disclosure hunting | No | None | MIT |
|
|
194
|
+
| 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 |
|
|
195
|
+
|
|
196
|
+
Full criteria, the fairness notes (behavioral checks catch
|
|
197
|
+
backports; crash signals also misfire on some patched builds),
|
|
198
|
+
SSRF-safety comparison and an FAQ:
|
|
199
|
+
**[PHP vulnerability scanners compared](https://phpatch.com/compare)**.
|
|
200
|
+
|
|
201
|
+
## What it doesn't do
|
|
202
|
+
|
|
203
|
+
- **It doesn't patch anything.** The scanner is read-only and
|
|
204
|
+
external; it cannot change your server.
|
|
205
|
+
- **It can't see a hidden version.** From outside, VERSION HIDDEN
|
|
206
|
+
is the honest end of the road — a definitive verdict for those
|
|
207
|
+
sites needs a read-only look on the server itself.
|
|
208
|
+
- **It doesn't monitor or alert.** It's a point-in-time check you
|
|
209
|
+
run; it won't notice next month's advisory for you.
|
|
210
|
+
- **It isn't a general security audit.** It judges the disclosed
|
|
211
|
+
PHP version against its advisory database — nothing more.
|
|
212
|
+
|
|
213
|
+
Patching, on-server verification, rollback, an evidence work
|
|
214
|
+
report, and monitoring are what the hosted service adds:
|
|
215
|
+
**[PHP Patch Assurance — phpatch.com](https://phpatch.com)**.
|
|
216
|
+
|
|
217
|
+
## Guides and further reading
|
|
218
|
+
|
|
219
|
+
- **[Self-hoster guides](https://phpatch.com/guides)** — update
|
|
220
|
+
PHP in Docker and with apt, mod_php vs PHP-FPM, rollback,
|
|
221
|
+
health checks, version checks, end-of-life upgrades, and
|
|
222
|
+
patching without downtime, written from the patch flow the
|
|
223
|
+
service itself uses.
|
|
224
|
+
- **[php2shell fix guide](https://phpatch.com/threats/php2shell)**
|
|
225
|
+
— what php2shell is, who is affected, how to check and patch,
|
|
226
|
+
the fixed releases, and the primary sources.
|
|
227
|
+
- **[Case study: mitbbs.ai](https://phpatch.com/case-studies/mitbbs-ai)**
|
|
228
|
+
— how a bilingual news portal patched php2shell on disclosure
|
|
229
|
+
night: 8.3.35 to 8.3.36 under mod_php, about 10 seconds of
|
|
230
|
+
downtime, verified live, no compromise found.
|
|
231
|
+
|
|
232
|
+
## Authorized use
|
|
233
|
+
|
|
234
|
+
Only scan sites you own or have permission to scan. The hosted
|
|
235
|
+
scanner at phpatch.com additionally enforces rate limits and an
|
|
236
|
+
opt-out blocklist for site operators (see the Terms, section 8).
|
|
237
|
+
|
|
238
|
+
## Advisory data
|
|
239
|
+
|
|
240
|
+
`phpscan/data/cves.json` is the advisory database the scanner
|
|
241
|
+
judges against — branch floors, fixed releases, references and the
|
|
242
|
+
leak/exploitation timeline. Last updated 2026-10-22. Update
|
|
243
|
+
process: floors are reviewed against the php.net release
|
|
244
|
+
announcements on every PHP security release and at least monthly.
|
|
245
|
+
Corrections are welcome as pull requests; every change to this
|
|
246
|
+
file requires maintainer review (see CODEOWNERS), because it
|
|
247
|
+
changes the verdicts the scanner gives.
|
|
248
|
+
|
|
249
|
+
A verdict of `patched` means only that the disclosed version is
|
|
250
|
+
not below the fixed releases in this database — it is not a
|
|
251
|
+
general security guarantee. A version below a floor on a
|
|
252
|
+
distribution build (Ubuntu/Debian packages) is a "check" signal,
|
|
253
|
+
not proof: distros backport fixes without changing the upstream
|
|
254
|
+
version, and the scanner says so when it detects a distro build.
|
|
255
|
+
|
|
256
|
+
## Support the project
|
|
257
|
+
|
|
258
|
+
phpscan is free and MIT-licensed, and the hosted scanner at
|
|
259
|
+
[phpatch.com](https://phpatch.com) is free for every site. If
|
|
260
|
+
the scanner, the threat desk or the daily Mega-Vuln Alert
|
|
261
|
+
reports helped you, you can chip in at
|
|
262
|
+
**[phpatch.com/donate](https://phpatch.com/donate)** — any
|
|
263
|
+
amount from $1, one-time, via Stripe. Donations fund phpscan
|
|
264
|
+
development, the free hosted scans and the public threat
|
|
265
|
+
research. Plainly stated on the page: PHPatch is operated by
|
|
266
|
+
Superstar Telecom LLC, a for-profit company, so donations are
|
|
267
|
+
voluntary support — not tax-deductible, and they buy no
|
|
268
|
+
product, service or entitlement.
|
|
269
|
+
|
|
270
|
+
## Scanned by phpscan badge
|
|
271
|
+
|
|
272
|
+
If you scan your site with phpscan, you can show it:
|
|
273
|
+
|
|
274
|
+
[](https://phpatch.com)
|
|
275
|
+
|
|
276
|
+
```markdown
|
|
277
|
+
[](https://phpatch.com)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
MIT. Not affiliated with the PHP Group. PHP is a trademark of the
|
|
283
|
+
PHP Group.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""phpscan — the free, open-source external PHP scanner from
|
|
2
|
+
PHP Patch Assurance (https://phpatch.com).
|
|
3
|
+
|
|
4
|
+
Passive fingerprinting plus limited, read-only checks of a few
|
|
5
|
+
well-known paths (the phpinfo exposure probes): it reads what a
|
|
6
|
+
site already discloses publicly (headers, generator tags, exposed
|
|
7
|
+
phpinfo pages) and judges any disclosed PHP version against the
|
|
8
|
+
advisory database in data/cves.json. It never runs exploit probes.
|
|
9
|
+
|
|
10
|
+
When a site hides its PHP version — most well-run sites do — the
|
|
11
|
+
verdict is an honest INDETERMINATE, with the path to a certain
|
|
12
|
+
answer (a read-only server check) explained at https://phpatch.com.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
__version__ = "1.4.4"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""CLI: python3 -m phpscan <url> [more urls...]
|
|
2
|
+
|
|
3
|
+
Prints one JSON verdict object per URL.
|
|
4
|
+
|
|
5
|
+
Exit codes: 0 — scanned, no actionable finding; 1 — at least one
|
|
6
|
+
target is vulnerable, possibly vulnerable, or on an end-of-life
|
|
7
|
+
branch; 2 — usage error, or a target could not be judged at all:
|
|
8
|
+
unreachable, or `not_assessed` (its PHP branch is newer than the
|
|
9
|
+
advisory database, so the scanner cannot judge it). When several
|
|
10
|
+
targets are scanned, the worst code wins.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
from .scan import scan
|
|
17
|
+
|
|
18
|
+
_ACTIONABLE = ("vulnerable", "possibly_vulnerable", "eol")
|
|
19
|
+
_UNJUDGED = ("not_assessed", "unreachable")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main(argv):
|
|
23
|
+
argv = [a for a in argv if a != ""]
|
|
24
|
+
if not argv:
|
|
25
|
+
print(__doc__)
|
|
26
|
+
return 2
|
|
27
|
+
if any(a in ("-h", "--help") for a in argv):
|
|
28
|
+
print(__doc__)
|
|
29
|
+
return 0
|
|
30
|
+
worst = 0
|
|
31
|
+
for url in argv:
|
|
32
|
+
try:
|
|
33
|
+
r = scan(url)
|
|
34
|
+
print(json.dumps(r, indent=1))
|
|
35
|
+
status = (r.get("verdict") or {}).get("status")
|
|
36
|
+
if status in _ACTIONABLE:
|
|
37
|
+
worst = max(worst, 1)
|
|
38
|
+
elif status in _UNJUDGED:
|
|
39
|
+
worst = max(worst, 2)
|
|
40
|
+
except Exception as e: # one bad target must not kill the run
|
|
41
|
+
print(json.dumps({"url": url, "error": str(e)}))
|
|
42
|
+
worst = max(worst, 2)
|
|
43
|
+
return worst
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def cli():
|
|
47
|
+
"""Console-script entry point (the `phpscan` command)."""
|
|
48
|
+
return main(sys.argv[1:])
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"advisories":[{"affected_sapis":["apache2handler (mod_php)","php-fpm","php-cgi","frankenphp","litespeed"],"cves":["CVE-2026-109618","CVE-2026-109619"],"cvss_v4":9.2,"distro_note":"Distribution packages may backport the fix without changing the upstream version string — verify via the package changelog or distro advisory, not the version alone. Third-party repositories lagged the fix on release day (Remi had no fixed build on 2026-10-21): when the repo has no fixed package, use the official container image or a source build, or apply the interim mitigations.","eol_below":"8.2","eol_note":"Branches below 8.2 receive no security fixes from the PHP project. There is NO patched release for them: the only remedies are a branch upgrade or taking the site offline (CERT-EU).","fixed_releases":{"8.2":"8.2.35","8.3":"8.3.36","8.4":"8.4.27","8.5":"8.5.12"},"id":"php2shell-2026-10","interim_mitigations":["Restrict access to the site (IP allow-list at the proxy / CDN) until patched — reduces exposure; it is not a fix","Enforce request-size limits at the reverse proxy / CDN edge as defense-in-depth","If no patched release can be deployed, take the site offline (CERT-EU guidance for unpatchable sites)"],"published":"2026-10-21","references":["https://www.php.net/releases/", "https://cert.europa.eu/publications/security-advisories/2026-018/", "https://www.cisa.gov/news-events/alerts/2026/10/21/critical-vulnerabilities-php-require-immediate-patching", "https://www.cve.org/CVERecord?id=CVE-2026-109618", "https://www.cve.org/CVERecord?id=CVE-2026-109619"],"summary":"Two critical flaws in PHP core: CVE-2026-109618 (remote code execution via out-of-memory corruption) and CVE-2026-109619 (remote code execution via stale global state). Both are exploitable over the network with no authentication. Any PHP application on a vulnerable build is exposed, regardless of the application itself.","timeline":"Vulnerability details leaked 2026-10-20. Fixed releases published 2026-10-21 (14:00 UTC). Exploitation was observed in the wild from 2026-10-21 07:43 UTC (Cloudflare data, reported by Sansec) — the exposure window opens at the leak, not at the patch.","title":"php2shell — unauthenticated remote code execution in PHP core"}],"source_notes":"Fixed releases published by the PHP project on 2026-10-21 (~14:00 UTC). CERT-EU advisory 2026-018, a CISA alert and an FBI alert circulated the same day; CERT-EU advised operators who cannot patch to take affected sites offline.","updated":"2026-10-22"}
|