2026 年 10 月,PHP 核心爆出兩個可在未登入狀態下遠端執行程式碼的漏洞(PHP2Shell,CVE-2026-109618/109619)。這篇整理我們一個小型 IT 團隊從「這個漏洞對我們有沒有影響」到「所有 PHP 主機修補完成」的實際過程,提供給同樣要面對這類事件的 IT 從業人員參考。
說明:本文已去識別化。公司名稱、人名、主機名稱、IP、雲端資源 ID 與內部系統名稱都已移除或改成通用代稱,例如「正式機」「RC 機」「測試機 A/B」。
一、漏洞速覽
| 名稱 | PHP2Shell(CVE-2026-109618、CVE-2026-109619) |
| 嚴重度 | CVSS v4.0 9.2,未登入的遠端程式碼執行,以 Web worker 帳號執行 |
| 成因 | 109618:記憶體不足時請求資料只初始化一半,清理階段誤當成有效資料;109619:前一個請求殘留的狀態,在同一個 worker 的下一個請求初始化前被使用 |
| 受影響 | 所有處理 Web 請求的 PHP(PHP-FPM、Apache mod_php、php-cgi、FrankenPHP、LiteSpeed);官方認為 mod_php 風險最高,已有在應用程式碼執行前就能觸發的 exploit |
| 修補版 | 8.5.12、8.4.27、8.3.36、8.2.35;8.1 以前官方不再修補(安全團隊僅提供 7.4/8.0/8.1 的最小原始碼 backport) |
這次比較特別的是:漏洞細節在官方修補前一天就外洩,網路上已經有攻擊工具,但套件庫要到修補發布後才會有新版。也就是說,有將近一天的時間,你只能靠「盤點+收斂入口+監控」撐住。
二、時間軸
| 相對時間 | 事件 | 我們做了什麼 |
|---|---|---|
| T−1 天 | 細節外洩,出現攻擊工具 | — |
| T−6.5 小時 | 主管詢問「對我們有沒有影響」 | 開工:定義範圍與「只能唯讀」的限制 |
| T−6 小時 | 內部技術討論 | 同仁用 AI 工具整理漏洞資訊,討論評估方向 |
| T−5.7 小時 | 曝險評估完成 | 四台 PHP 主機與防火牆規則唯讀檢查,寫成內部紀錄 |
| T−5.2 小時 | 全員公告 #1 | 發布去除細節的簡化版公告 |
| T−4.8 小時 | 收斂第一個入口 | 經核准後,移除 RC 機對全網開放的防火牆規則 |
| T | 官方釋出修補 | — |
| T+50 分 | 確認官方公告 | 讀官方公告與 GitHub Security Advisory,查套件庫是否上架(尚未) |
| T+1 小時 | 全員公告 #2 | 說明修補已釋出、IT 接下來的步驟 |
| T+9.5~19 小時 | 依序升級 | 測試機 A → 測試機 B → RC 機 → 正式機,容器映像同步重建 |
| T+10~22 小時 | 三次複查 | 唯讀驗證每台主機與容器;擴大範圍檢查所有對外主機 |
三、第一步:唯讀盤點,不急著改
事件一開始,最容易犯的錯是「先改再說」。我們給自己的第一條規則是:評估階段全程唯讀——不用 sudo、不跑 apt update、不重啟服務、不改防火牆。原因很實際:沒有完整的現況,任何變更都可能修錯地方,或在正式機上製造新的事故。
每台主機我們都跑同一支唯讀腳本,確認這幾件事:
- CLI 與 Web 實際使用的 PHP 版本(兩者可能不同)
- Web 是 Apache mod_php 還是 PHP-FPM(決定風險高低)
- 主機上裝了幾個 PHP 版本(沒在用的也是攻擊面)
- 容器裡的 PHP 版本(主機修好不代表容器修好)
- 本機套件快取裡可升級到哪一版(不跑
apt update,只看快取) - 監聽中的 Web 埠
# Read-only PHP exposure check. No sudo, no package updates, no writes.
php -v | head -1
ls /etc/php
dpkg-query -W -f='${Package} ${Version}\n' | grep -E '^(php[0-9.]+-(common|cli|fpm)|libapache2-mod-php)'
ls /etc/apache2/mods-enabled | grep -i php
systemctl list-units --type=service --all --no-pager | grep -i php
docker ps --format '{{.Names}} {{.Image}}'
for c in $(docker ps --format '{{.Names}}'); do docker exec "$c" sh -c 'command -v php >/dev/null && php -v | head -1'; done
apt-cache policy php8.4-common # local cache only
ss -ltn | awk 'NR>1{print $4}' | grep -E ':(80|443|8080|9000)$'
雲端這一側,則用 CLI 唯讀列出每台主機掛的 Security Group,找出對 0.0.0.0/0 開放 80/443 的規則。
四、曝險分級:版本只是其中一個因素
| 主機 | Web 堆疊 | 對外入口 | 風險 |
|---|---|---|---|
| 正式機 | Apache + mod_php | 80/443 對全網開放,可繞過 CDN/WAF 直連 | 最高 |
| RC 機 | Apache + mod_php | 80/443 對全網開放(當初為了憑證驗證) | 高 |
| 測試機 A | Apache + mod_php | 只開白名單與 CDN | 中 |
| 測試機 B | nginx + PHP-FPM | 只開白名單 | 中 |
四台的 PHP 版本其實都在受影響範圍內,真正拉開差距的是 SAPI 和 對外入口。特別值得注意的是「可繞過 CDN/WAF 直連 origin」:就算你在 WAF 加了防護規則,攻擊者只要直接打主機 IP 就能繞過。
盤點時另一個發現:四台主機都還沒套用前一個月就已釋出的 PHP 安全更新(修了 11 個 CVE)。這跟這次漏洞無關,但說明例行更新的節奏落後了。
五、修補版出來之前,能做什麼
1. 收斂入口(最有效)
我們先處理 RC 機:它的 80/443 對全網開放,只是為了當初的憑證自動驗證,但現在的憑證已經不需要這條規則。移除之前,我們先確認了三件事:
- CDN 的 origin 是負載平衡器,負載平衡器走 VPC 內網連到主機,內網規則還在;
- 辦公室與合作夥伴的白名單在其他 Security Group 裡;
- DNS 沒有直接指向這台主機公網 IP 的紀錄。
確認後才卸下那個 Security Group(不是刪除,因為另一台主機還在用),接著驗證:經 CDN 回 200、負載平衡器的 target 全部 healthy。整個變更與 rollback 指令都寫進變更紀錄。
2. 官方的暫時措施(只能降低風險,不能取代更新)
session.upload_progress.enabled=0,設在 php.ini 或 pool/server 設定並重啟所有 worker(ini_set()與.user.ini無效);- 以
php_admin_value設memory_limit=-1,並調低post_max_size; - 不需要上傳功能的路徑,在前端擋 multipart/form-data;
- 不要用
RLIMIT_AS/RLIMIT_DATA,反而會觸發漏洞路徑。
3. 加強監控
盯 Web Server access log 與 CDN/WAF log,特別是異常的 multipart POST。
六、修補版出來之後
1. 先讀官方公告,再動手
修補前流傳的資訊有一部分是錯的(例如受影響範圍)。我們在官方發布後 50 分鐘內讀完 php.net 公告與兩則 GitHub Security Advisory,再更新內部評估。
2. 套件庫有自己的時差
我們的 PHP 來自第三方 PPA,官方晚上發布,PPA 隔天早上才上架。修補時效的上限,往往是你的套件來源決定的。可以用 Launchpad API 直接查 PPA 的發布狀態,不用一直 apt update。
3. 依序升級:測試 → RC → 正式
4. 驗證「真的載入新版」
php -v 顯示新版,不代表 Apache 已經載入新的 mod_php。我們用一個簡單的方法唯讀確認:比對 /var/log/dpkg.log 的升級時間,和 Apache/PHP-FPM master 程序的啟動時間。master 程序的啟動時間晚於套件升級時間,才算真的換上新版。
grep -E ' upgrade (php8|libapache2-mod-php)' /var/log/dpkg.log | tail
ps -eo pid,ppid,lstart,comm --no-headers | awk '$2==1' | grep -E 'apache2|php-fpm'
有一台正式機我們連續複查了兩次都還是舊版——主機的 apt 快取停在前一版、Apache master 從一週前跑到現在。若只看「有沒有下升級指令」,很容易誤判已修好。
5. 容器要另外修
- 主機升級不會影響容器裡的 PHP,基底映像要重建、容器要重新部署。
- 我們發現一個陷阱:新映像用了新 tag,但舊的通用 tag 沒有更新。之後只要有人用舊 tag 部署,就會拉回未修補的版本。
- 前端容器重建了,但同一個服務的 PHP 後端容器沒有一起重建——複查時才抓到。
- 沒有權限看的容器(需要 sudo),可以用
ps加/proc/<pid>/cgroup先看到容器內有哪些程序、何時重啟,至少知道「有沒有被重新部署過」。
七、複查時把範圍放大
四台主機修好後,我們做了一次「整體再評估」,把範圍從「已知的 PHP 主機」擴大到「所有執行中、對全網開放 80/443 的主機」,結果多找到 7 台:
- 其中有幾台的啟動映像是 Ubuntu 14.04(2019 年就結束標準支援),映像名稱還標著 PHP 5.6、PHP 7.4;
- 還有一台是「已經宣告退役」的舊環境,但主機仍在執行、仍對外開放。
這些主機遇到同類漏洞時,根本沒有修補版可以裝。事件應變的最後一步,往往會挖出比原本問題更大的技術債。
八、溝通:同一件事,三種版本
| 對象 | 內容 | 原則 |
|---|---|---|
| IT 內部 | 完整評估:主機、版本、入口、證據、待辦 | 可追溯,每一步都留紀錄與變更 LOG |
| 主管 | 風險、建議處置、需要決定的事 | 讓主管能做決定:核准什麼、何時維護 |
| 全員 | 漏洞是什麼、對公司的影響、IT 在做什麼、同仁要配合什麼 | 不寫主機名稱、版本與哪台還對外開放——公開這些等於替攻擊者畫重點 |
全員公告用「#1、#2 … #FIN」編號,讓大家知道這是同一件事的第幾報,也知道什麼時候算結案。
九、AI 在這次事件中的角色
這次我們大量使用 AI 助理,但分工很清楚:
- 人決定界線與核准:「只能唯讀」、哪個變更可以做、公告怎麼寫、何時發,都是人決定。整個事件中唯一的基礎設施變更,是在明確核准後才執行。
- AI 負責廣度與一致性:同一支唯讀腳本跑多台主機、跨帳號列出所有對外規則、讀官方公告與 Advisory、把散在聊天室、CI、Git、筆記裡的證據串起來,並起草紀錄與公告。
- 結果:從「有沒有影響」到評估、公告、修補後複查,在一天內完成,而且每一步都有紀錄可查。
要提醒的是:AI 工具以你的帳號權限執行。登入正式機、讀取雲端設定這類工作,務必先把「唯讀」寫成明確限制,變更一律逐項核准,敏感資料(密碼、金鑰、個資)不要出現在對話或輸出裡。
十、學到的事
- 跟上 Active Support,是最便宜的資安投資。這次能在一天內修完,最主要的原因是主機跑的 PHP 還在官方支援期內。停在 EOL 版本,再積極也只剩隔離、停機或硬撐。
- 例行更新要有固定節奏。上個月的安全更新沒裝,緊急時要處理的落差就更大。
- 入口收斂比版本更急。能繞過 WAF 直連 origin 的規則,常常是「當初為了某件事臨時開的」。
- 「退役」要真的關掉。宣告退役不會讓對外入口消失。
- 驗證要看「執行中的東西」:master 程序啟動時間、容器內的
php -v,而不是只看套件版本。 - 容器與建置鏈也算正式環境。映像 tag、基底映像、CI 用的 Node/Composer 版本都要一起跟上。
- 分層溝通,公開版不給細節。
附錄:應變檢查清單
- □ 定義範圍與限制(評估階段唯讀)
- □ 每台主機:PHP 版本(CLI/Web)、SAPI、已安裝版本、容器版本、可升級版本、監聽埠
- □ 雲端:對全網開放的 80/443、可繞過 CDN/WAF 的 origin
- □ 依「SAPI × 對外入口 × 版本」分級
- □ 修補前:收斂入口(先確認流量路徑與 rollback)、暫時措施、監控
- □ 分層公告(內部完整版、主管決策版、全員簡化版)
- □ 官方公告發布後:先讀公告與 Advisory,再查套件來源是否上架
- □ 依序升級:測試 → RC → 正式
- □ 驗證:dpkg.log 升級時間 vs master 程序啟動時間;容器內
php -v - □ 容器:重建基底映像、更新或淘汰舊 tag、確認同服務的所有容器都已重建
- □ 擴大範圍複查:所有對外主機、EOL 作業系統與執行環境、已退役但仍在執行的主機
- □ 移除未使用的 PHP 版本
- □ 發布結案公告(#FIN),把技術債排入計畫
參考資料
- php.net — Security advisory: Critical remote code execution vulnerabilities, update now
- GitHub php-src — GHSA-w8hw-w9fx-j79w(CVE-2026-109618)
- GitHub php-src — GHSA-6wmf-4rv5-p3rp(CVE-2026-109619)
- php.net — Supported Versions
- Tenable — PHP2Shell FAQ
- endoflife.date — 各軟體支援期限查詢
本文由作者及其 AI 助理協作完成。