> ## Content Index
> Fetch the complete content index at: https://taiwanding.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# PHP2Shell 來了，先看清楚你跑的是哪個 PHP
- URL: https://taiwanding.com/php2shell-lai-liao-xian-kan-qing-chu-ni-pao-de-shi-na-ge-php/
- Published: 2026-10-22T04:22:24.000Z
- Updated: 2026-10-22T04:44:57.000Z
- Author: Kevin Chen
- Tags: #php, php2shell, #claude code, #RCE

10 月 21 日，PHP 官方釋出緊急安全更新，修補兩個被稱為 PHP2Shell 的重大漏洞。隔天一早我打開終端機，做的第一件事是開一個對話視窗，跟 Claude Opus 說：「昨天有很嚴重的 PHP 漏洞，一起檢查一下我的主機。」

所以這篇的解法第一步就是：找 AI 一起討論 XD。

開玩笑的，但也不全是玩笑，這次討論真正有價值的，不是哪一行指令，而是過程中一直被問到的幾個問題：「你確定主機上只有這一個 PHP 嗎？」「這個版本號可信嗎？」「更新完，實際在跑的是新版嗎？」這篇想把這幾個問題整理出來，讓你不管用什麼平台，都能自己檢查一遍。

## 先講結論

如果你只有五分鐘：

- 把 PHP 升到修補版：8.5.12、8.4.27、8.3.36、8.2.35，8.1 和更舊的版本，PHP 官方不會再發布正式修補版本。
- 更新完一定要重啟 PHP-FPM 或 Apache，套件換了，舊的程序還在記憶體裡跑舊程式碼。
- 確認「實際在跑的」是新版，而不是「看起來」是新版。
- 修不了的那個，至少不要讓它直接暴露在網路上。

剩下的篇幅，是在講怎麼做到這四件事，以及我在兩台主機上踩到的坑。

## PHP2Shell 是什麼

PHP2Shell 是 CVE-2026-109618 和 CVE-2026-109619 兩個漏洞的合稱，CVSS 都是 9.2，攻擊者不用登入就能在伺服器上遠端執行程式碼，只要是對外提供網頁的 PHP 都在範圍內，包含 PHP-FPM、Apache 的 mod\_php、php-cgi、FrankenPHP 和 LiteSpeed。

有兩件事值得特別記住：

**問題在 PHP 執行環境本身，不在你的程式碼**，所以只更新 WordPress 或外掛修不掉它；反過來說，如果只是在同一個版本分支內更新（例如 8.4.26 升到 8.4.27），通常不需要修改網站程式碼，可以想成汽車召回：要換的是引擎裡的零件，車子本身不用動，但如果你原本的版本已經沒有修補，要跨大版本升級（例如從 7.4 升到 8.4），就可能遇到相容性問題，要另外測試。

**細節在修補釋出前一天就外洩了**，也就是說，修補出來的時候，已經有人拿著細節在掃，這種時候要假設「我的主機可能已經被看過」，修完之後也要回頭檢查。

## 為什麼不能只給你一行指令

官方的建議很簡單：升級到修補版，但「怎麼升級」每台主機都不一樣，因為 PHP 可能從好幾個地方來：

- 作業系統的套件（Ubuntu、Debian 官方）
- 控制面板自己的套件庫（CloudPanel、cPanel、Plesk 等）
- Docker image
- 自己編譯

我的兩台主機就剛好是兩種完全不同的狀況，所以與其給你指令，不如給你五個要回答的問題，指令只是範例，換到你的平台再對應調整。

## 問題一：我的主機上到底有哪些 PHP 在跑？

這是最重要的一步。

```bash
# 系統套件裝了哪些 PHP
dpkg -l | grep -E '^ii\s+(php|libapache2-mod-php)'

# 有哪些 PHP-FPM 或 Apache 服務在跑
systemctl list-units --type=service --no-pager | grep -iE 'php|apache'

# 每一個 PHP-FPM 主程序，連控制面板自己藏的那個也會現形
ps -eo pid,user,cmd | grep 'php-fpm: master' | grep -v grep

# Docker 裡的服務
docker ps --format '{{.Names}}\t{{.Image}}'

```

第三行特別推薦，服務名稱可能取得千奇百怪，但一般 PHP-FPM 的主程序，通常會顯示為 `php-fpm: master process`，後面括號還會告訴你它用哪個設定檔，我就是靠這一行，才發現控制面板的後台自己跑著一個舊版 PHP。

如果你用 Docker，還要多想一步：`docker ps` 只會列出「正在跑」的 container，如果你有隨需求才開的環境（例如 CTF 題目、預覽環境），它們現在沒在跑，就不會出現在清單裡，所以先把停止的 container 和所有 image 都列出來：

```bash
# 包含已停止的 container
docker ps -a --format '{{.Names}}\t{{.Image}}\t{{.Status}}'

# 主機上所有的 image
docker image ls

```

從名稱看起來可能有 PHP 的（例如 `php`、`wordpress`、`apache` 開頭的，或自己 build 的題目 image），再個別檢查：

```bash
docker run --rm --entrypoint sh [image 名稱] -c 'command -v php php-fpm'

```

要注意這個指令會用那個 image 實際啟動一個暫時的 container，跑完就刪掉，而且如果 image 裡沒有 `sh`，或 PHP 不在預設的 PATH 裡，它可能什麼都沒印出來，但不代表裡面沒有 PHP，所以這只是輔助，不是百分之百可靠的判斷。

最後，如果同一台主機有好幾個 PHP 版本，要確認每個網站實際用的是哪一個，每個網站可能使用獨立的 FPM pool，也可能好幾個網站共用預設的 pool，所以不能只憑 pool 設定檔的數量判斷某個版本有沒有在用，要從網站伺服器的設定，確認請求實際轉發到哪個 PHP-FPM socket：

```bash
# nginx 把 PHP 請求轉發到哪裡
sudo nginx -T 2>/dev/null | grep -E 'fastcgi_pass|proxy_pass'

# 每個 PHP 版本的 pool 在哪裡接收請求
grep -rn '^\s*listen\s*=' /etc/php/*/fpm/pool.d/

```

把兩邊對起來，就知道每個網站實際交給哪個版本處理，用 Apache 的話，要看的是網站設定裡 `SetHandler` 或 `proxy:unix:` 指向的 socket。

## 問題二：版本號可信嗎？

直覺上會用 `php -v` 看版本，但這裡有兩個陷阱。

```bash
dpkg -l 'php8.3*' | grep ^ii
zgrep -lE 'CVE-2026-10961[89]' /usr/share/doc/php8.3-*/changelog.Debian.gz

```

**陷阱一：發行版的版本號不會跟著上游走**，Ubuntu 修漏洞的方式是把修補「移植」進原本的版本，所以 Ubuntu 24.04 修好之後，`php -v` 還是顯示 8.3.6，要看的是套件版本（Ubuntu 24.04 的修補版是 `8.3.6-0ubuntu0.24.04.13`），或是直接在套件的變更紀錄裡找 CVE 編號：

反過來，控制面板自己打包的 PHP 通常會跟著上游版本號（例如 `8.4.27-1+...`），這時 `php -v` 就是準的。所以要先知道你的 PHP 是誰打包的，才知道版本號能不能信。

**陷阱二：`php -v` 看的是命令列版本，不是網站在用的那個**，網站是由 PHP-FPM 在跑，要另外看 FPM 的執行檔：

```bash
php-fpm8.4 -v | head -1

```

不過這看的還只是磁碟上那個檔案的版本，正在跑的程序是不是已經換成它，問題四會再確認。

如果是控制面板自己的 PHP，連執行檔是哪一個都不一定看得出來，可以用問題一拿到的 PID 反查：

```bash
sudo readlink /proc/[PID]/exe

```

## 問題三：修補從哪裡來？

在 Debian 系的主機上，`apt policy` 可以告訴你這個套件會從哪個來源安裝：

```bash
apt policy php8.4-fpm

```

輸出裡每個版本後面都有一個優先權數字，也會列出來源網址，這次我遇到的狀況是：控制面板把自己的套件庫優先權設成 1000，比 Ubuntu 官方高，所以 **Ubuntu 的修補版根本裝不進來**，只能等控制面板推出新版。

這件事平常沒有感覺，但在緊急修補的時候就很關鍵：你的修補速度，取決於幫你打包 PHP 的那一方。

另外，PHP 8.1 和更舊的版本已經結束上游官方支援，不會再發布正式的新版本，不過 PHP 官方這次特別提供了 7.4、8.0、8.1 的原始碼修補，部分發行版也有回移植安全更新，例如 Ubuntu 22.04 的 php8.1 套件就在 `8.1.2-1ubuntu2.28` 修補了這次的漏洞，還在用這些版本的話，要看你的發行版或面板廠商有沒有提供，不然就是要換到 8.2 以上，反過來說，就算 `php -v` 顯示的是 8.1 的版本號，也不能直接斷定有沒有被修過，一樣要看套件版本和維護商的公告。

## 問題四：真的換成新版了嗎？

套件更新完不等於修好了，PHP-FPM 是常駐程式，套件換了，記憶體裡的舊程序還在跑舊的程式碼，一定要重啟：

```bash
sudo apt update
sudo apt install --only-upgrade 'php8.4*'
sudo systemctl restart php8.4-fpm

```

然後確認四件事：

```bash
# 1. 磁碟上的執行檔是修補版
php-fpm8.4 -v | head -1

# 2. 服務是在更新之後才重新啟動的（看啟動時間）
systemctl status php8.4-fpm --no-pager | grep Active

# 3. 正在跑的主程序用的是哪個執行檔
sudo readlink /proc/$(systemctl show -p MainPID --value php8.4-fpm)/exe

# 4. 網站有回應
curl -sI https://[你的網域] | head -3

```

第 3 項如果顯示 `/usr/sbin/php-fpm8.4 (deleted)`，代表程序還在用已經被套件換掉的舊執行檔，也就是還沒真正重啟到新版，要再重啟一次，第 1 項說是新版、第 2 項說是剛重啟、第 3 項沒有 `(deleted)`，三個對得上，才能說正在跑的是修補版，第 4 項只確認 HTTP 有回應，網站功能正不正常，還是要用瀏覽器實際測。

這裡有個容易漏掉的地方：如果控制面板的後台用的是自己的 PHP 服務，它的服務名稱通常不叫 `php*-fpm`，用萬用字元重啟不會碰到它，要個別處理。

最後別忘了用瀏覽器實際點一下網站，特別是表單、上傳、快取這些依賴擴充套件的功能，這次就有社群回報，某個面板的修補版第一次打包時漏了幾個擴充套件，更新完網站功能就壞了，後來才重新打包補上。

## 問題五：修不了的，怎麼縮小它的暴露面？

總會有暫時修不了的東西，這次我遇到的就是控制面板後台的 PHP 8.1，這時候的思路是：既然修不了，就縮小它能被碰到的範圍，但要記得，這是降低風險的暫時措施，不是修好了。

**停掉沒在用的版本**，我那台主機十個 PHP 版本全部都在跑，實際上只有一個有網站在用，沒在用的就停用並 mask，避免開機時又被帶起來，停用前一定要先照問題一的方法，從 nginx 或 Apache 的設定確認沒有網站把請求轉發到這些版本，不要只看 pool 設定檔，判斷錯了會直接把正在用的網站停掉（版本清單換成你確認過沒在用的）：

```bash
for v in 7.1 7.2 7.3 7.4 8.0 8.1 8.2 8.3 8.5; do
  sudo systemctl disable --now php$v-fpm
  sudo systemctl mask php$v-fpm
done

```

注意是停用而不是移除，有些控制面板的套件會把每個 PHP 版本都列為必要依賴，用 apt 移除舊版本會連面板一起拔掉，之後要重新啟用，用 `systemctl unmask` 就能解開，停完之後，也記得馬上確認網站還正常。

**關掉不需要對外的埠**，管理後台通常不需要對全世界開放。先看防火牆規則：

```bash
sudo ufw status numbered

```

刪除規則時要照原本的寫法刪。我原本以為規則是 `8443/tcp`，刪了才發現對不到，仔細一看規則寫的是 `8433:8443/tcp`，當初設定時打錯一個數字，結果一口氣開了 11 個埠。

刪完之後，要確認 IPv4 和 IPv6 的規則都不見了，再從外部實際連一次，確認真的連不上：

```bash
# 在主機上：IPv4 和 IPv6 的規則都要消失
sudo ufw status | grep 8443

# 在自己的電腦上：應該要逾時或被拒絕
nc -zv -w3 [主機 IP] 8443

```

設定檔寫的是你以為的樣子，從外面實際連一次，才是真正的樣子。

**改用 SSH 通道進後台**，埠關掉之後，後台還是用得到，只是要先開 SSH 通道：

```bash
ssh -L 8443:127.0.0.1:8443 [管理帳號]@[主機 IP]
# 然後瀏覽器開 https://localhost:8443

```

原理是：SSH 的 22 埠一直開著，通道從這裡進到主機裡面，再從主機本機連後台，而防火牆預設不會擋主機自己連自己，這樣後台就不再直接暴露於公網，必須先通過 SSH 身分驗證才能從外部建立通道，能大幅降低遠端攻擊風險，如果你的 SSH 還允許密碼登入，建議順便改成只允許金鑰。

但 PHP 漏洞本身仍然存在，它還是在主機上跑著，主機上其他服務如果有 SSRF 之類的漏洞，或攻擊者已經從別處取得本機存取權限，還是可能從內部連到它，所以這只是暫時的隔離措施，不能取代安全更新。

如果家裡是浮動 IP，這個做法會比「防火牆只允許自己的 IP」更不容易把自己鎖在外面，萬一 SSH 也出問題，大部分 VPS 業者的管理介面都有網頁版主控台，可以從那裡進去救援。

## 回頭檢查空窗期

因為細節在修補前就外洩了，修完之後我也回頭看了 10 月 19 日之後的狀況：

```bash
# 網站目錄裡有沒有新增或被改過的 PHP 檔（webshell 最常見的落地方式）
sudo find /var/www /home/*/htdocs -name '*.php' -newermt '2026-10-19' -ls 2>/dev/null

# PHP 有沒有異常崩潰（記憶體破壞類漏洞被嘗試時可能留下的痕跡之一）
sudo journalctl -k --since '2026-10-19' --no-pager | grep -i segfault

# 最近的登入紀錄
last -i | head -10

```

`find` 有列出檔案也先別緊張，網站程式自動更新或快取也會產生新檔，逐一確認是不是正常的就好。崩潰紀錄也不代表一定被打穿，但如果很多，就值得配合存取紀錄往下追，反過來，沒有崩潰紀錄也不代表沒事，成功的利用不一定會讓 PHP 崩潰，也不一定會寫入 webshell。

我兩台都是跑完沒有可疑輸出，登入紀錄也都是我自己。

不過，這些只是初步排查，沒有發現異常不代表能完全排除入侵，`last` 看的是登入紀錄，透過 HTTP 漏洞進來的攻擊不會出現在這裡，若需要更深入確認，還要檢查 nginx 或 Apache 的存取紀錄、PHP 錯誤紀錄、異常程序與對外連線。

## 我的兩台主機

### 主機 A：以為有，其實沒有

第一台是 Ubuntu 24.04，跑部落格和幾個 Docker 服務，照問題一盤點下來：主機上沒有裝任何 PHP 套件，結論是 PHP2Shell 跟這台無關。

但盤點的過程還是有收穫：它提醒我有一個會隨需開 container 的服務，那些環境平常不會出現在 `docker ps` 裡，要另外檢查 image，也順手把系統其他的安全更新補完了。

如果我一開始就假設「我有 PHP，趕快升級」，會白忙一場；假設「我沒有 PHP，不用管」，又可能漏掉隨需開的那些，先盤點，才知道要做什麼。

### 主機 B：以為一個，其實十一個

第二台用 CloudPanel 管理，跑一個 PHP 網站，我原本的認知是「這台有一個 PHP」，盤點完的實際狀況是：

- 十個 PHP-FPM 版本（7.1 到 8.5）全部都在跑
- 只有 8.4 設定了網站的 pool，停用其他版本之後網站也運作正常
- 還有第十一個：CloudPanel 後台自己的 PHP，跑在 8.1.34，PHP 官方不會再發布修補版本，CloudPanel 的套件庫也還沒有更新，而且直接對外開在 8443

處理順序是：

1. 把 8.4 升到 8.4.27，重啟，確認網站正常
2. 停用並 mask 其他九個沒在用的版本
3. 發現後台卡在 8.1，修補版短期內不會出來
4. 把後台的埠從防火牆關掉（順便抓到那條打錯的規則），改走 SSH 通道
5. 回頭檢查空窗期，沒有發現異常

到這裡，後台已經不再直接暴露在公網上，風險大幅降低，但舊版 PHP 的漏洞本身還在，所以我後來還是想辦法把後台也換到 8.4.27，這段牽涉到 systemd 和 FastCGI 的運作方式。

## 跟 AI 一起處理的心得

回到開頭的玩笑，這次整個過程不到一個小時，我的做法是把終端機的輸出原封不動貼給 Claude，讓它判讀、建議下一步，它也會即時去查最新狀況，例如控制面板 GitHub issue 裡大家回報的修補進度，這部分比我自己一篇篇翻快很多。

但它也不是每次都對，它一開始用 `sudo -v` 判斷某個帳號有完整的 sudo 權限，後來看到 `sudo -l` 才修正；也曾經猜主機上的資料庫是舊網站留下來的，後來才想到部落格本身就需要它，所以我的原則是：每一步的輸出自己都要看懂，看不懂就問「為什麼」，搞懂了才往下做，尤其是在正式環境，指令跑下去之前，要知道它會改到什麼、出事要怎麼還原。

下篇的內容，就是我在過程中一直追問「為什麼可以這樣做」問出來的。

## 檢查清單

- \[ \] 盤點主機上所有的 PHP：套件、服務、FPM master process、Docker（包含已停止的 container 和 image）
- \[ \] 從 nginx 或 Apache 的設定，確認每個網站實際轉發到哪個 PHP-FPM
- \[ \] 確認你的 PHP 是誰打包的，版本號能不能信
- \[ \] 確認修補從哪個來源來，來了沒有
- \[ \] 更新後重啟，從正在跑的主程序確認是修補版（沒有 `(deleted)`），網站功能正常
- \[ \] 確認沒有網站轉發過去之後，才把沒在用的版本停用並 mask
- \[ \] 不需要對外的埠關掉（IPv4、IPv6 都要確認，並從外部實際測試），管理後台改走 SSH 通道
- \[ \] 回頭檢查 10 月 19 日之後的新增檔案、崩潰紀錄、登入紀錄

## 參考資料

- [PHP2Shell: Critical PHP vulnerabilities patched, update now! - Help Net Security](https://www.helpnetsecurity.com/2026/10/21/php2shell-php-vulnerabilities-patched-cve-2026-109618-cve-2026-109619/?ref=taiwanding.com)
- [php2shell (CVE-2026-109618 and CVE-2026-109619) – PHP critical vulnerability fixes available - Ubuntu](https://ubuntu.com/blog/php2shell-vulnerability-fixes-available?ref=taiwanding.com)
- [USN-8948-1: PHP vulnerabilities - Ubuntu](https://ubuntu.com/security/notices/USN-8948-1?ref=taiwanding.com)
- [PHP2Shell: AI uncovers critical RCE vulnerabilities - Wiz](https://www.wiz.io/blog/php2shell-php-rce?ref=taiwanding.com)
- [Upcoming Critical PHP Security Release - CloudPanel Issue #823](https://github.com/cloudpanel-io/cloudpanel-ce/issues/823?ref=taiwanding.com)
- [Feature request: upgrade the PHP version used by CloudPanel's own php-fpm - CloudPanel Issue #816](https://github.com/cloudpanel-io/cloudpanel-ce/issues/816?ref=taiwanding.com)