
2026 年 10 月 21 日,日本首相高市早苗在 X 上發了一篇文,請全國的網站管理者當晚就確認一件事:你的網站有沒有在用 PHP。
那篇貼文到現在 5,698 萬次查看、28.9 萬個讚。
一位首相為了一個程式語言的修補檔發文叫人準備,這種畫面很不尋常...
她在文裡講得很白:修補檔日本時間當晚 11 點發布,請大家盡快套用;如果套不上,把網站暫時關掉也是降低風險的有效做法。
我當天晚上把這件事從頭到尾查了一遍,也在自己的機器上把漏洞跑了一輪。結論先講:這次的洞不在你的網站程式碼,在 PHP 引擎本身。
兩個洞,都是引擎的錯
PHP 官方在 2026 年 10 月 21 日釋出 8.5.12、8.4.27、8.3.36、8.2.35 四個版本,修補兩個 CVSS 4.0 都是 9.2 的漏洞:
CVE | 問題 | 後果 |
|---|---|---|
CVE-2026-109618 | 記憶體不足(OOM)時的處理會把請求狀態留在半毀的狀態 | 未驗證的遠端程式執行 |
CVE-2026-109619 | 上一個請求的狀態沒清乾淨,被下一個請求讀到 | 洩漏其他請求的記憶體,或執行任意程式碼 |
受影響的是所有會處理網頁請求的 PHP 安裝:PHP-FPM、Apache mod_php、php-cgi、FrankenPHP、LiteSpeed 全在名單上。PHP 官方特別點名 mod_php 風險最高,因為那裡有一個已經被證實的手法,未驗證的請求可以在應用程式碼執行之前就觸發攻擊。
受影響與修好的版本:
分支 | 受影響 | 修好 |
|---|---|---|
8.2 | 8.2.0 到 8.2.34 | 8.2.35 |
8.3 | 8.3.0 到 8.3.35 | 8.3.36 |
8.4 | 8.4.0 到 8.4.26 | 8.4.27 |
8.5 | 8.5.0 到 8.5.11 | 8.5.12 |
8.1 及更舊的版本,PHP 專案已經不維護了,只有少數 Linux 發行版自己 backport。至於 PHP 5.6 這種等級,連 backport 都沒有。
為什麼 WordPress 更新到最新也沒用
這是整件事最容易被誤解的地方。
php2shell 的洞在 PHP 引擎:請求進來之後,PHP 要先解析 HTTP 標頭、解析表單與上傳檔案、初始化 session,這些全都在你的 index.php、functions.php、任何一支外掛被載入之前就做完了。攻擊就發生在這一段。
所以你的 WordPress 更新到最新、外掛全部更新、主題也更新,對這兩個洞一點幫助都沒有。PHP 官方在 advisory 裡寫得很直接:一個只有 Hello World 的網頁,跑在受影響的版本上,一樣會中。
Wiz 研究團隊的示範影片拿 WordPress、Drupal、Joomla 當靶機,只是因為這三個最常見、最好架,不代表只有這三個會被打。
我自己跑了一次:8.4.26 與 8.4.27 的差別
我沒有拿真實網站來試。原因後面講,先講我在本機看到的東西。
環境是本機自己編的 PHP,php -S 起一個單進程的伺服器,單一 worker 輪流處理請求,這一點很重要,因為第二個洞需要的正是同一個 worker 接手前一個請求留下的殘骸。memory_limit 設 16M,靶機的 upload.php 只有一行 echo "SCRIPT_RAN";。
攻擊是這樣組的:一個 multipart POST,夾一個 PHP_SESSION_UPLOAD_PROGRESS 欄位(讓 session 這一條路徑先跑起來),再用一個檔案欄位塞七萬個 200 bytes 的假標頭。網路上是 14.3 MB,但 PHP 內部要為每個標頭建雜湊項與兩份字串,膨脹倍率大約三到五倍,14 MB 就足以撐爆 16 MB 的上限。
結果:
版本 | 同一個請求,各跑 5 次 | 應用程式碼有執行嗎 |
|---|---|---|
PHP 8.4.26(未修補) | 5 次全部 OOM | 0 次,一次都沒跑到 |
PHP 8.4.27(已修補) | 0 次 | 5 次都正常執行 |
8.4.26 的錯誤長這樣:
Fatal error: Allowed memory size of 16777216 bytes exhausted at
Zend/zend_smart_str.c:165 (tried to allocate 256 bytes) in Unknown on line 0
要看的是最後一句:in Unknown on line 0。沒有檔案、沒有行號,因為當時根本還沒有任何 PHP 程式碼在跑。
我後來把標頭加到 25 萬個(53 MB)丟給 8.4.27,結果一樣無感,應用程式照常執行。原因在修補的內容:舊版遇到記憶體爆掉是用 longjmp() 直接跳出去,修補版改成延後到安全點處理,真的撐過第二道硬上限才直接結束進程。同一個攻擊,在修好的版本上只是被當成一個太大的請求。
為什麼我不拿真實網站做示範
這一段是我決定寫這篇的主要原因,因為我知道會有人問:你不是有網站嗎,怎麼不直接打給大家看。
第一,我的站都在共享主機上。同一台機器上還有別的客戶的站,把 OOM 打進去的意思,是讓主機的 PHP worker 反覆爆掉、重啟,在防火牆眼裡跟 DDoS 沒兩樣。帳號被停是最可能的結果,而且會連累別人。
第二,公開的 exploit 不存在。Wiz、OpenAI、Calif 的利用程式沒有公開,PHP 那邊釋出的是修補,不是攻擊工具。我手上只有原理,沒有能一鍵打穿的東西。硬要對真實網站打,跑出來的畫面是「失敗」,把它包裝成「攻擊演示」就是造假。
第三,真的打到 RCE,中間還有一大段路。advisory 自己也標了高複雜度與前置條件,要控制記憶體佈局、要對上同一個 worker、要多次嘗試。那不是一支 curl 能完成的事,硬做只會寫出一篇自己都不信的文。
第四,本機反而看得更清楚。單一 worker、固定變數、可以重跑、可以對照版本,log 全部留著。要講機制,這種環境比打一個真實網站更有說服力。
所以我寫的是「重現了觸發」,不是「駭進去了」。
前者我能拿出 log,後者我沒有,就不寫。
你現在該做什麼
先確認你是不是那 70%。指令一行:
curl -sI https://你的網域 | grep -i x-powered-by有 PHP/8.x.xx 就是跑 PHP,版本號直接看得出來。看不到標頭不代表沒跑 PHP,只代表它被關掉了。
再對照上面的版本表。比我表格裡「修好」那一欄舊的,就是有事。
如果主機商沒給你選版本的權限,那就寫信去要,這不是你一個人能解決的事。等待的期間,可以做臨時緩解,但要知道它不等於修補:
# mod_php / LiteSpeed(.htaccess)
php_value session.upload_progress.enabled 0 php_value memory_limit -1 php_value post_max_size 32M
; PHP-FPM(.user.ini,.htaccess 的 php_value 對 FPM 無效)
session.upload_progress.enabled = 0 memory_limit = -1 post_max_size = 32M
session.upload_progress.enabled=0 切掉的是 109619 那條已經被證實的路徑。PHP 官方自己也說這個功能幾乎沒人用、很少被測試,關掉最省事。memory_limit=-1 則是拆掉 109618 那顆可控的引信,代價是記憶體失控時改由作業系統收拾,所以還是要升級。
結論
php2shell 的洞在 PHP 引擎,不在 WordPress:CVE-2026-109618 與 CVE-2026-109619,CVSS 皆 9.2,修好版本是 8.2.35、8.3.36、8.4.27、8.5.12
本機實測:同一個 14.3 MB 的 multipart 請求,PHP 8.4.26 五次全部在 body 解析階段 OOM,應用程式碼一次都沒執行(錯誤訊息 in Unknown on line 0);8.4.27 五次全部正常
你現在能做的:用 x-powered-by 查版本,低於修好版本的請主機升級,等待期間關掉 session.upload_progress 並設 memory_limit=-1
漏洞不在你寫的程式裡,在你沒寫的那一段。更新外掛救不了它,只能把 PHP 換掉。
原文發布於 2026-10-22。測試環境為本機自編 PHP,未對任何線上網站發送攻擊請求。
技術細節引用 PHP 官方安全公告(2026-10-21)與 php2shell.com。
౨ৎ Anthony 不負責任編輯 ~
留言 (0)