php2shell-audit 实战:Windows 用 exe、Linux 用 py,十分钟摸清 PHP 那两个 RCE 有没有打到你
这篇不绕弯子,全部是能直接敲的东西:装工具、跑本机、扫目标、读结论、判断要不要打补丁,一条龙。十分钟能查完。
先说清前提:CVE-2026-109618 和 CVE-2026-109619 是 PHP 引擎本身的未授权 RCE,官方修复版本是 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12。评分有两套口径——PHP 官方用 CVSS 4.0 打 9.2,NVD 用 CVSS 3.1 打 9.8,标题里的 9.8 是后者,两个都指同一对漏洞,别以为是两回事。
工具在这儿:viviopenclaw-code/php2shell-audit,Apache-2.0,今天刚发的。它做两件事:查本机装了什么 PHP、扫远端暴不暴露版本。它不发攻击流量、不写盘、也不改任何配置,这一点我在源码里核过,远程模式真的只发一个 HEAD 请求读响应头。

一、装:两条路,看你在哪个系统
Linux / macOS 走 Python 版,零依赖,标准库就够,不用 pip 装任何东西:
git clone https://github.com/viviopenclaw-code/php2shell-audit.git
cd php2shell-audit
chmod +x php2shell_audit.py
python3 php2shell_audit.py --local
Windows 直接下 exe,不用装 Python。在 release 页面拿 php2shell-audit.exe:
certutil -hashfile php2shell-audit.exe SHA256
php2shell-audit.exe --local
exe 是单文件控制台程序,Win10/11 x64 可用,没有代码签名,所以第一次跑 SmartScreen 会拦。先跑 certutil 算一下哈希,跟同一个 release 里的 SHA256SUMS.txt 对一遍——这一步别省,网上流传的“破解版”“绿色版”一律不要碰。
两个版本的参数完全一致,教程后面只写一套,Windows 把 python3 php2shell_audit.py 换成 php2shell-audit.exe 就行。

二、跑本机:–local 到底查了什么
python3 php2shell_audit.py --local
它会做四件事,一件不落:
- 找 PHP:
/usr/bin/php*、/usr/sbin/php-fpm*、/opt/*、/usr/lib/php/*/sapi/*,Homebrew 也算。Windows 上查C:\php、XAMPP、WAMP、Laragon、Chocolatey、Scoop、Program Files 下的 PHP。 - 读版本:跑
php -v拿 banner,跟 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 比。 - 查配置:解析 php.ini 里那几条临时缓解指令。
- 列进程:把打完补丁必须重启的 worker 全列出来。
我在一台没装 PHP 的机器上跑了一遍,输出是这样的:
php2shell-audit 1.1.0 - local audit
Host : admin
System : Linux 7.0.0-30-generic (x86_64)
------------------------------------------------------------------------
[UNKNOWN] No PHP installation detected.
------------------------------------------------------------------------
php.ini mitigation directives
[WARNING] session.upload_progress.enabled = (unset) (recommended 0)
[WARNING] fatal_error_backtraces = (unset) (recommended 0)
[WARNING] expose_php = (unset) (recommended 0)
------------------------------------------------------------------------
Running services that must be restarted after patching
[INFO] apache2 - 3 process(es)
------------------------------------------------------------------------
[INFO] No PHP binary, FPM daemon or Apache PHP module was found on this host.
------------------------------------------------------------------------
Remediation
[UNKNOWN] No PHP install was audited, so nothing can be confirmed safe.
注意最后那行:没装 PHP 它报 UNKNOWN,不报 PASS。这个设计我很认可——没查到东西和查了没问题,是两码事,混成一个“绿色勾”就是耍流氓。
顺带说,它连 expose_php 都提示了。这条跟漏洞本身没关系,但直接决定了远程扫描能不能读到你的版本号——关掉它,攻击者的指纹阶段就瞎一半。当然这不叫修复,叫减少信息暴露。
三、参数就这几个

| 参数 | 作用 | 默认 |
|---|---|---|
--local / --remote | 二选一,决定查本机还是查目标 | 必填 |
--file targets.txt | 批量目标,一行一个,# 开头为注释 | 无 |
--workers N | 远程并发线程数 | 10 |
--json report.json | 输出完整 JSON,- 打到 stdout | 无 |
--timeout N | 单请求超时(秒) | 8 |
--insecure | 跳过 TLS 校验,仅用于指纹 | 关闭 |
退出码:能直接当 CI 卡点
| 退出码 | 含义 |
|---|---|
0 | 没发现受影响版本 |
2 | 至少发现一个受影响版本 |
1 | 工具自身出错(比如 JSON 写不出来) |
退出码 2 这一条是重点。意味着你可以把它塞进流水线,构建阶段先扫一遍,命中就 fail,别等上线后靠人肉发现。
四、扫远程:–remote 怎么用
# 单个目标
python3 php2shell_audit.py --remote example.com
# 批量,20 个并发,出 JSON
python3 php2shell_audit.py --remote --file targets.txt --workers 20 --json out.json
# 也可以直接写在命令行里
python3 php2shell_audit.py --remote https://a.example 10.0.0.5
targets.txt 怎么写
# production fleet
example.com
https://shop.example.net
10.0.0.5:8080
# 开头是注释,一行一个,仓库里有 targets.example.txt 可以直接抄。
它到底发了什么
每个目标只发一个 HEAD 请求,只有服务器拒绝 HEAD 时才会退化成一次 GET。然后读 Server:、X-Powered-By: 和 Set-Cookie,从里面抠出 PHP/x.y.z。
不发 multipart、不发超大 body、不触发 OOM、不碰 use-after-free。它在源码注释里把这条当成设计约束写死了,不是随口承诺。
代价也得说清楚:远程模式证明不了可利用性,它只能报告服务器自愿吐出来的信息。隐藏版本号的目标,结果永远是 UNKNOWN。
五、怎么读结论

[VULNERABLE] https://shop.example.net
http : 200
server : Apache/2.4.62 (Debian)
powered : PHP/8.4.16
php : 8.4.16
verdict : PHP 8.4.16 < fixed 8.4.27
- [VULNERABLE] 版本读到了,且低于修复版本。
- [PASS] 版本读到了,且大于等于修复版本。
- [WARNING] 临时缓解指令没按建议设置。
- [UNKNOWN] 证据不足。不是安全,是不知道。
这个区分是整个工具最值钱的地方。绝大多数扫描器只会给你红黄绿三色,然后你对着满屏绿色以为天下太平。UNKNOWN 必须当成待办,要么去关掉版本隐藏、要么进容器里跑 --local、要么登录机器看。
六、四个坑,一个比一个实在

坑 1:UNKNOWN ≠ 安全
藏了版本号的主机,永远只会报 UNKNOWN。真要确认,只能在主机上跑 --local。
坑 2:发行版 backport 会误报
Debian / Ubuntu / RHEL / Alpine 经常把安全补丁打进去但不跳版本号。结果就是补过了的机器,照样显示 [VULNERABLE]。
所以看到红字先别慌,先查发行版 changelog:
apt changelog php8.4
rpm -q --changelog php-8.4 | head -50
这一步不做就升级,等于白折腾一轮;做了还误判,那就是真需要升级。
坑 3:一个 HEAD 也是请求
它会进对方日志,会被 WAF 记下来。没拿到书面授权就扫,性质上是未授权探测——CFAA、英国 CMA 1990 这类法条都管得着。
扫自己公司的资产也得先拿授权,尤其是跨部门、跨子公司、云上多账号的场景。别觉得“我只是看个响应头”就不算。
坑 4:--local 只查本机
PHP 跑在容器里,就进容器里跑,或者在宿主机上用 --remote 打容器映射出来的端口。宿主机上跑 --local 查不到容器里的 PHP,会给你一个 UNKNOWN——那个 UNKNOWN 是真的不知道,不是容器安全。
七、查出来有问题,然后呢
打补丁是唯一完整修复。其他全是止血。
# Debian / Ubuntu
sudo apt update && sudo apt install --only-upgrade php
# RHEL / Fedora
sudo dnf upgrade 'php*'
# Alpine
sudo apk upgrade php83 php84
升级版本对照:8.2 分支到 8.2.35,8.3 到 8.3.36,8.4 到 8.4.27,8.5 到 8.5.12。8.1 及更早已停更,只有源码级 backport。
上不了补丁就先做这几条,然后重启所有 worker
; CVE-2026-109619
session.upload_progress.enabled = 0
; CVE-2026-109618
memory_limit = -1
; 仅 PHP 8.5
fatal_error_backtraces = 0
session.upload_progress.enabled=0要写在 php.ini、FPM pool 或服务器配置里,必须早于请求解析生效。ini_set() 和 .user.ini 都太晚。memory_limit=-1要用php_admin_value这类应用改不动的级别设,同时保证没有 pool / 目录 / 应用把它改回有限值。- 千万别用 RLIMIT_AS 或 RLIMIT_DATA——那俩会让 mmap 失败,正好重新走进那条危险的 longjmp 路径。
- 改完必须重启每一个 worker:
systemctl restart php*-fpm apache2 nginx。不重启等于没改。
这四条没有一条能让你变安全,官方自己都说得很明白。它们的价值只有一个:在你打补丁之前,把门先虚掩上。
八、完整流程串一遍
- 本机先自查:
--local,三五秒出结果,看版本和配置。 - 远端批量扫:整理 targets.txt,
--remote --file并发跑,出 JSON。 - 分类处理:VULNERABLE 排期升级,WARNING 补配置,UNKNOWN 逐个查清——UNKNOWN 不是结案,是待办。
- 升级 + 重启 worker:打补丁,然后重启所有 FPM / Apache / nginx。
- 复扫验证:再跑一遍,确认全绿。注意 backport 误报,必要时看 changelog。
- 接进流水线:退出码 2 直接 fail 构建,下次就不用靠人记着。
整个过程十分钟够了,真正花时间的其实是第三步——把 UNKNOWN 一条条查清。但这一步不能省,因为跳过它,你拿到的就是一张漂亮的、假的、全绿报表。
扫描器的价值不在给你绿灯,在逼你承认哪些地方你其实不知道。
项目地址再放一次:github.com/viviopenclaw-code/php2shell-audit。用之前把 DISCLAIMER.md 读一遍,中文的,写得很实在。
最后一句提醒:这工具今天是 1.1.0 首个公开版本,一星零 fork,代码量不大,你可以自己通读一遍 php2shell_audit.py——跑在服务器上的东西,看一眼源码不算过分要求,尤其是这种要拿管理员权限去读 php.ini 和进程列表的。
没有啦 (T▽T)
延伸阅读:
php2shell-audit 实战:Windows 用 exe、Linux 用 py,十分钟摸清 PHP 那两个 RCE 有没有打到你
只读、不发攻击流量、一个文件零依赖。本机自查、远端批量、三种结论怎么读、四个必踩的坑,外加升级后必须重启 worker ...
Java 出了个 9.1 分漏洞 CVE-2026-91403,可我照着 Oracle 公告查了一圈,发现两个大坑
它不在这一季的 CPU 里,而那个 9.1 分里也没有 RCE 这一项。把 Oracle 的公告体系、CVSS 向量、供...
PHP2Shell 炸了:CVSS 9.2,公开前一天就有人在打,而这两个洞在代码里躺了十年
CVE-2026-109618 和 CVE-2026-109619,PHP 5.4 到 8.5 全线受影响,未授权 RC...
76.2 万条微信全导出来喂给 AI:最难的压根不是 AI
76.2 万条消息、3675 万字、十年记录。导出、清洗、分块、向量化、混合检索,五步管道全流程,外加五个必踩的坑。还有...
opencode-models-discovery 2.0.0 发布:配置形状全换,OpenCode v2 正式转正
九种元数据格式、原生 /connect 凭证、宿主托管缓存。从 v1 迁到 v2 的配置字段对照和四条检查清单都在这里。