資安與漏洞

PHP2Shell 是什麼?CVE-2026-109618、109619 為何一次被判 9.2 分

admin · 10月22日 · 2026年 · · · · · 本文共3755个字 · 预计阅读13分钟33次已读

一句話直答:CVE-2026-109618 與 CVE-2026-109619 是 PHP 核心的兩個記憶體毀損漏洞,2026 年 10 月 21 日公告,兩個的 CVSS v4.0 分數都是 9.2(Critical),社群合稱 PHP2Shell。未登入的遠端攻擊者只要送出一個特製的 multipart 請求,就有機會在你的網頁伺服器上執行任意指令,而且整個過程發生在應用程式被載入之前,你的程式寫得再乾淨都不影響。

PHP2Shell 是什麼?CVE-2026-109618、109619 為何一次被判 9.2 分

這兩個 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 這個分數不是喊出來的,有五個結構性理由疊起來:

  1. 不需要任何身分:無需登入、無需 session,一個匿名 POST 就能觸發。
  2. 位置在應用程式之前:PHP 先解析進來的請求資料,才初始化擴充、才編譯你的第一行 PHP。攻擊成不成立與你網站跑什麼完全無關,維護良好的站與放生的站風險一樣。
  3. 預設設定就會中:session upload progress(session.upload_progress.enabled)預設是 On,這是已確認的觸發路徑之一,也是為什麼多數預設環境直接符合前提。
  4. 攻擊是無聲的:worker 不會崩潰、伺服器照回 HTTP 200、應用程式日誌沒有異常。你很難從既有監控看出被打了。
  5. 範圍橫跨七年以上:可觸發的程式碼最早可追到 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 這條路徑。

PHP2Shell 是什麼?CVE-2026-109618、109619 為何一次被判 9.2 分

時程也值得記下來:原定公開日是 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,但它已含修補。看套件版號與發行版公告才準,下一篇會完整拆開這個追蹤方法。

接下來看這兩篇

如果你負責的機器不只一台,建議現在就做兩件事:把「每一台機器上實際服務中的 PHP 版本」列成一張表,然後把這篇的檢查步驟跑一遍。有結果歡迎留言告訴我們你遇到哪種版本陷阱。覺得有用請轉給那位還在用 PHP 7.4 的朋友,他會需要這篇。

0 条回应