一句話直答:CVE-2026-109618 與 CVE-2026-109619 是 PHP 核心的兩個記憶體毀損漏洞,2026 年 10 月 21 日公告,兩個的 CVSS v4.0 分數都是 9.2(Critical),社群合稱 PHP2Shell。未登入的遠端攻擊者只要送出一個特製的 multipart 請求,就有機會在你的網頁伺服器上執行任意指令,而且整個過程發生在應用程式被載入之前,你的程式寫得再乾淨都不影響。
這兩個 CVE 到底是什麼
先看編號對照,再看白話說明。
| 項目 | CVE-2026-109618 | CVE-2026-109619 |
|---|---|---|
| 類型 | 記憶體耗盡(OOM)處理不當導致的 RCE | 請求狀態跨 worker 重用導致的 RCE |
| 弱點分類 | CWE-824(使用未初始化的指標) | CWE-824(使用未初始化的指標) |
| CVSS v4.0 | 9.2(Critical) | 9.2(Critical) |
| GitHub 公告 | GHSA-w8hw-w9fx-j79w | GHSA-6wmf-4rv5-p3rp |
| 受影響版本 | PHP 5.4 至 8.5.11 之間的所有版本 | PHP 5.4 至 8.5.11 之間的所有版本 |
| 修補版 | 8.5.12、8.4.27、8.3.36、8.2.35 | 8.5.12、8.4.27、8.3.36、8.2.35 |
兩者共用同一個根因。PHP 遇到致命狀況(記憶體超過 memory_limit、執行逾時、客戶端斷線)時,不是沿著呼叫堆疊正常回傳錯誤,而是用 longjmp() 直接拋棄當下的執行現場。任何正在組裝資料結構的 C 函式就這樣被丟掉:解構子不執行、記帳不回滾、也沒人記錄這個結構是不完整的。接著 PHP 走進一段「假設所有結構都完整」的例行清理流程,問題就從這裡開始。
- CVE-2026-109618:在請求記憶體還沒初始化完成時被打斷。資料結構已經被標記為「含有一筆項目」,但支撐那筆項目的記憶體還沒配置;當這次配置正好撞上記憶體上限,那一格就留著前一次遺留的堆積資料,而失敗請求自己的清理程式會去讀它、並依它做分支判斷。
- CVE-2026-109619:跨請求的模組級全域變數還指向「已經被釋放」的每請求堆積記憶體,而且仍回報自己處於運作中。同一個 worker 處理的下一個請求,會在正常初始化覆蓋它之前讀到或依它分支,是典型的 use-after-free。
為什麼是 Critical,不是 High
9.2 這個分數不是喊出來的,有五個結構性理由疊起來:
- 不需要任何身分:無需登入、無需 session,一個匿名 POST 就能觸發。
- 位置在應用程式之前:PHP 先解析進來的請求資料,才初始化擴充、才編譯你的第一行 PHP。攻擊成不成立與你網站跑什麼完全無關,維護良好的站與放生的站風險一樣。
- 預設設定就會中:session upload progress(
session.upload_progress.enabled)預設是 On,這是已確認的觸發路徑之一,也是為什麼多數預設環境直接符合前提。 - 攻擊是無聲的:worker 不會崩潰、伺服器照回 HTTP 200、應用程式日誌沒有異常。你很難從既有監控看出被打了。
- 範圍橫跨七年以上:可觸發的程式碼最早可追到 PHP 5.4(2012 年),其中一個前提由 2017 年的效能優化引入、7.3.0 首次出貨。也就是說,每一條分支都受影響,「版本很舊」不等於沒人會打。
攻擊面涵蓋所有處理網頁請求的 SAPI:PHP-FPM、Apache 的 mod_php、php-cgi、FrankenPHP、LiteSpeed。已驗證的 RCE 場景是 Apache 加 mod_php,官方公告另外記錄了 CVE-2026-109619 在 PHP-FPM 下已驗證的利用路徑。
攻擊者實際上會怎麼打
已知的觸發都是同一種形狀:multipart form-data 請求,也就是瀏覽器送出檔案上傳時用的那種。公開的 PoC 手法包含幾種加壓方式:不帶 Content-Length 的 multipart 請求(可以繞過 post_max_size 這道限制)、標頭數量不受限、大量空檔案欄位(max_file_uploads 不計算空檔案,那些結構照樣吃記憶體)、以及超長的檔案名稱欄位。搭配 PHP_SESSION_UPLOAD_PROGRESS 欄位就能踏進 upload progress 這條路徑。
時程也值得記下來:原定公開日是 10 月 22 日 14:00 UTC,因技術細節外流提前一天到 10 月 21 日 14:00 UTC。修補釋出後數小時內,資安廠商就在誘捕系統上看到實際的探測與利用嘗試,Cloudflare 也回報早期攻擊並持續更新邊緣緩解。這不是「等有空再修」的漏洞。
打進去之後,攻擊者取得的是網頁伺服器使用者的指令執行權,常見下一步是落地 webshell、讀取設定檔裡的資料庫帳密、對外打內網或塞挖礦程式。修補是唯一的完整解法,因為漏洞在你的程式碼之前就成立。
不修會有什麼後果
| 你的情境 | 不修的直接後果 |
|---|---|
| 對外服務的 WordPress 或自架站 | 被掃到即可能被取得指令執行權,站台被種後門、掛廣告或跳轉 |
| 有上傳功能的應用 | multipart 是已知觸發形狀,等於天天有人在敲的那道門 |
| 內網系統 | 風險略低但不等於安全,任何一台可對外的跳板都能打進來 |
| EOL 分支(8.1、8.0、7.4 及更舊) | 沒有正式修補版,只能自行編譯官方回移植分支或換版,留在原地等於長期暴露 |
還有一個常見誤解要打破:更新 WordPress、外掛或佈景主題,都不會修掉這個問題。這是伺服器端 PHP 執行環境的漏洞,只有 PHP 本身換成修補版才算處理。如果你用虛擬主機或代管服務,請直接向服務商確認他們的 PHP 執行環境是否已經更新。
現在就能跑的檢查步驟
先確認「真正回答網頁請求的那個 PHP」是哪個版本,再談要不要修。
# 1. 所有 PHP 執行環境的版本(CLI 的答案不代表網站用的那個)
php -v | head -1
php-fpm -v 2>/dev/null | head -1
ls -1 /usr/bin/php* /opt/*/php* 2>/dev/null
# 2. 正在服務的 php-fpm 到底是哪一支執行檔、什麼時候啟動的
ls -l /proc/$(pgrep -o php-fpm)/exe
ps -o lstart= -p $(pgrep -o php-fpm)
# 3. 檢查是否已有探測跡象(upload progress 欄位是已知路徑之一)
grep -rl "PHP_SESSION_UPLOAD_PROGRESS" /var/log/nginx /var/log/apache2 2>/dev/null | head
# 4. 目前這版是否還在受影響範圍(對照上表:8.5.12/8.4.27/8.3.36/8.2.35 才是修補版)
php -r 'echo PHP_VERSION, " ", PHP_SAPI, PHP_EOL;'
如果一時無法更新,官方與各廠商列的過渡緩解可以爭取時間,但要清楚它們不是解法:
- 關閉
session.upload_progress.enabled:擋掉其中一條已確認路徑,其他變體仍在。設定要寫進 php.ini、FPM pool 或伺服器設定,用ini_set()或單一請求的 .user.ini 不可靠。 - 調小
post_max_size:對 CVE-2026-109618 有用(它靠耗盡記憶體觸發)。無上傳需求設 8M,有上傳需求設 32M,並讓upload_max_filesize低於它;max_input_vars、max_file_uploads也一併收緊。 - 在邊緣層或 WAF 阻擋
Content-Type: multipart/form-data:會直接弄壞檔案上傳,且要涵蓋所有 HTTP 方法(PHP 8.4 以上連非 POST 也會解析 multipart)。 - FPM 設
pm.max_requests = 1:讓 worker 更頻繁回收,降低跨請求狀態殘留的機率,只是降低不是消除。
多數廠商都強調這些措施無法完全阻擋利用;真的排不出時間修補,隔離或下線會比撐著安全。
常見問題
我有 Cloudflare 或 WAF,是不是就沒事?
不是。邊緣防護能擋掉當下已知的特徵,廠商也確實在數小時內更新了規則,但那是持續的貓抓老鼠。真正關掉攻擊面的方式仍然是更新 PHP。
我的站只在內網,可以先放著嗎?
內網降低的是被直接掃到的機率,不是漏洞本身。任何反向代理、VPN 入口或測試環境跳板都能把攻擊送進來,分數 9.2 又無聲,不值得賭。
我的 PHP 是 8.3.6,看起來很舊,是還沒修嗎?
這是最容易誤判的一點。發行版套件版號不一定跟著上游走,例如 Ubuntu 24.04 的 php8.3 修補版是 8.3.6-0ubuntu0.24.04.13,前面數字仍是 8.3.6,但它已含修補。看套件版號與發行版公告才準,下一篇會完整拆開這個追蹤方法。
接下來看這兩篇
- PHP2Shell 從哪來?CVE、GHSA、USN 與上游分支怎麼追:四種編號體系的用途、EOL 分支的官方回移植是什麼、如何確認手上版本。
- PHP2Shell 修補與實戰升級 SOP:修補版清單、7.4 自行編譯流程、逐步升級與回滾、以及我們實際踩到的三個坑。
如果你負責的機器不只一台,建議現在就做兩件事:把「每一台機器上實際服務中的 PHP 版本」列成一張表,然後把這篇的檢查步驟跑一遍。有結果歡迎留言告訴我們你遇到哪種版本陷阱。覺得有用請轉給那位還在用 PHP 7.4 的朋友,他會需要這篇。


