← 返回列表

php2shell-audit 實戰:Windows 用 exe、Linux 用 py,十分鐘摸清 PHP 那兩個 RCE 有沒有打到你

itadol5j

這篇不繞彎子,全部是能直接敲的東西:裝工具、跑本機、掃目標、讀結論、判斷要不要打補丁,一條龍。十分鐘能查完。

先說清前提: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 請求讀響應頭。

php2shell-audit 終端運行效果

一、裝:兩條路,看你在哪個系統

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 就行。

Linux 與 Windows 兩種安裝方式

二、跑本機:–local 到底查了什麼

python3 php2shell_audit.py --local

它會做四件事,一件不落:

  1. 找 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。
  2. 讀版本:跑 php -v 拿 banner,跟 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 比。
  3. 查配置:解析 php.ini 裏那幾條臨時緩解指令。
  4. 列進程:把打完補丁必須重啓的 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 都提示了。這條跟漏洞本身沒關係,但直接決定了遠程掃描能不能讀到你的版本號——關掉它,攻擊者的指紋階段就瞎一半。當然這不叫修復,叫減少信息暴露。

三、參數就這幾個

php2shell-audit 參數一覽
參數作用默認
--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、要麼登錄機器看。

六、四個坑,一個比一個實在

使用 php2shell-audit 的四個注意事項

坑 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。不重啓等於沒改。

這四條沒有一條能讓你變安全,官方自己都說得很明白。它們的價值只有一個:在你打補丁之前,把門先虛掩上。

八、完整流程串一遍

  1. 本機先自查:--local,三五秒出結果,看版本和配置。
  2. 遠端批量掃:整理 targets.txt,--remote --file 併發跑,出 JSON。
  3. 分類處理:VULNERABLE 排期升級,WARNING 補配置,UNKNOWN 逐個查清——UNKNOWN 不是結案,是待辦。
  4. 升級 + 重啓 worker:打補丁,然後重啓所有 FPM / Apache / nginx。
  5. 復掃驗證:再跑一遍,確認全綠。注意 backport 誤報,必要時看 changelog。
  6. 接進流水線:退出碼 2 直接 fail 構建,下次就不用靠人記着。

整個過程十分鐘夠了,真正花時間的其實是第三步——把 UNKNOWN 一條條查清。但這一步不能省,因爲跳過它,你拿到的就是一張漂亮的、假的、全綠報表。

掃描器的價值不在給你綠燈,在逼你承認哪些地方你其實不知道。

項目地址再放一次:github.com/viviopenclaw-code/php2shell-audit。用之前把 DISCLAIMER.md 讀一遍,中文的,寫得很實在。

最後一句提醒:這工具今天是 1.1.0 首個公開版本,一星零 fork,代碼量不大,你可以自己通讀一遍 php2shell_audit.py——跑在服務器上的東西,看一眼源碼不算過分要求,尤其是這種要拿管理員權限去讀 php.ini 和進程列表的。