返回資料庫
安全與治理

SHA 釘選不等於驗證:Plugin4Shell 與首個 AI 代理供應鏈 RCE

最後更新:2026年9月18日

925 個插件 skill 已被從原始維護者手中劫持,而這些 skill 波及 134,000 個代理。2026 年 9 月 17 日,AIR Security 揭露了 Plugin4Shell — AI 代理生態系的首個供應鏈漏洞:一個零點擊遠端程式碼執行(RCE)漏洞同時影響 Claude Code、OpenAI Codex、GitHub Copilot 與 Google Gemini CLI,全部源於同一處缺失的檢查。其機制是每個受影響代理都略過的一條 git 斷言:代理檢出 marketplace 釘選的 commit SHA,卻從不驗證工作樹真正落在該 commit 上。控制插件儲存庫的攻擊者以釘選的 40 字元 SHA 為分支命名,將其設為儲存庫預設分支,代理便在回報「乾淨安裝於釘選 commit」的同時安裝了攻擊者控制的程式碼(Cyber Security News;Help Net Security)。

這對任何針對生產系統執行編碼代理的團隊都至關重要,因為釘選本身就是那個控制點。超越社群 marketplace 的組織 — 審查插件程式碼、將安裝釘選到已審查的 commit — 一直依賴 SHA 釘選作為防護,而 Plugin4Shell 悄然使其失效:審查通過、釘選寫入、執行的卻是不同的程式碼(AIR)。本文涵蓋一位工程負責人在下一次插件自動更新觸發前需要瞭解的四件事:一條 git 命令講清的機制、從良性採用到背景 RCE 的五步攻擊、廠商回應記分板(兩家已修復、一家未修復、一家棄用且未修復),以及為代理安裝的每個釘選構件 — 插件、MCP 伺服器、skill — 堵住缺口所需的驗證項。

關鍵要點

  • 零點擊 RCE 透過同一處缺失的 git 斷言影響了全部四個主流編碼代理 — Claude Code、Codex、GitHub Copilot 與 Gemini CLI — AIR 於 2026 年 9 月 17 日揭露 Plugin4Shell:代理檢出 marketplace 釘選的 commit SHA,卻從不驗證檢出結果是否解析到了該 commit(AIR)。
  • 925 個 skill 已被從維護者處劫持,波及 134,000 個代理 — AIR 的 SkillJacking 研究;Plugin4Shell 擊穿的正是為遏制此類儲存庫劫持而構建的 SHA 釘選機制(AIR)。
  • Git 偏向與雜湊同名的分支而非雜湊本身 — 一條以釘選的 40 字元 SHA 為名、被設為儲存庫預設分支的分支,會把 git checkout <sha> 重新導向到攻擊者程式碼;Gemini CLI 的變體則以 FETCH_HEAD 為攻擊面(Cyber Security News)。
  • 廠商記分板:2 家已修復、1 家未修復、1 家棄用且未修復 — Anthropic 在 2.1.179 修復了 Claude Code,OpenAI 在 0.146.0 修復了 Codex;Microsoft 尚未發布 Copilot 修復,Google 棄用 Gemini CLI 且未提供修補,意味著所有現有安裝持續暴露(Help Net Security)。
  • 一條斷言即可堵住兩種變體:檢出後驗證解析出的 HEAD 是否等於釘選 SHAtest "$(git rev-parse HEAD)" = "<pinned-sha>" || abort — 且該檢查必須在代理內部執行,因為任何 marketplace 都無法強制執行一個它並不解析的釘選(AIR)。

下方圖表將這次揭露壓縮為一分鐘:五步攻擊鏈、三天後的廠商記分板,以及堵住缺口的那條斷言。

SHA 釘選不等於驗證 Plugin4Shell:一條缺失的 git 斷言,四大編碼代理全部零點擊 RCE — AIR,2026 年 9 月 17 日 完整攻擊鏈 1 埋點 良性插件釘選於 commit aaa…aaa, 通過審查。 2 採用 安裝將 pin 寫入 已審查、可信的 commit。 3 重新釘選 例常更新:marketplace 把 pin 推進到 bbb…bbb。 4 變臉 名為 bbb…bbb 的分支 成為預設分支 — 指向攻擊程式碼。 5 RCE 自動更新檢出該分支。 無點擊、無提示、 悄無聲息。 廠商回應 — 2 家已修復、1 家未修復、1 家棄用未修復 Anthropic Claude Code 已修復 2.1.179 版修復 OpenAI Codex 已修復 0.146.0 版修復 Microsoft GitHub Copilot 無修復 GitHub 的命名禁令:不夠 Google Gemini CLI 已棄用 未修復 — 遷移至 Antigravity 修復 — 檢出後驗證 test "$(git rev-parse HEAD)" = "<pinned-sha>" || abort 1. 檢查解析出的 HEAD — 工作樹真正包含的內容 — 而非請求的 ref。 2. 檢查必須在代理內部執行:pin 在客戶端解析,任何 marketplace 都無法代為強制執行。 3. 無補丁處(Copilot、Gemini CLI)在補丁發布前限制代理的可達範圍。 劫持經濟的規模(AIR) 925 skill 被劫持 134,000 波及代理 4/4 主流代理受影響 2/4 廠商未修復 從未驗證的釘選只是一紙建議。先驗證檢出 — 再更新代理, 因為在沒有補丁的地方,釘選背後沒有任何保證。 Plugin4Shell — AI 代理供應鏈的首個漏洞 — ideabosque.com/library

機制:一條斷言,四個代理

SHA 釘選本該按設計運轉。審查者在某個 commit 上審查插件,marketplace 記錄該 commit 的 SHA,代理理應永遠安裝恰好是那段程式碼的版本。失敗發生在最後一步。每個受影響代理都會執行大致如下的序列(AIR):

git clone  ./
git checkout aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa   # 釘選的 SHA

卻從不追問檢出是否真正落在釘選的 commit 上。這一遺漏之所以可利用,是因為 git 先解析名稱後解析物件:當一個名稱既是合法 ref 又是 commit 雜湊時,git 偏向 ref,只列印一條 refname is ambiguous 警告。已經控制上游儲存庫的攻擊者建立一條以精確的 40 位十六進制釘選 SHA 為名的分支,並將其設為儲存庫預設分支。一次普通的 git clone 會把該分支拉為本地 ref,git checkout <sha> 解析到該分支,工作樹便被攻擊者控制,而代理回報「在釘選 SHA 上成功安裝」(Cyber Security News)。

兩個條件使這一手法成立。第一,沒有任何普遍規則阻止「名為雜湊的分支」:git 自己的 check-ref-format 接受 40 位十六進制名稱,雖然 GitHub 直接拒絕,但 Bitbucket 和自託管 git 伺服器允許 — 而 Anthropic 自己的文件把 Bitbucket 與自託管 git 列為有效的 marketplace 後端(AIR)。第二,該分支必須是儲存庫預設分支;非預設分支只會以遠端跟蹤 ref 的形式到達,checkout 會退回真實 commit。

Gemini CLI 的失敗方式不同。它的安裝序列先擷取釘選的 commit,再檢出 FETCH_HEAD:

git clone --depth 1  ./
git fetch origin 41d0bc0a4aeb2fbf797dacea39e876d98c95024b
git checkout FETCH_HEAD

如果儲存庫預設分支本身就叫 FETCH_HEAD,檢出會解析到該分支並靜默丟棄擷取的 commit(AIR)。命令不同,根因相同:代理信任自己請求的名稱,而不驗證自己收到的 commit。

五步攻擊:從採用到 RCE

兩條攻擊路徑都不需要控制 marketplace。AIR 演示了兩條:

  1. 埋點。 攻擊者發布一個真正良性的插件,釘選在 commit aaa…aaa。它通過審查。
  2. 採用。 使用者安裝。每次安裝都釘選到已審查的 commit。
  3. 重新釘選。 攻擊者推送一次仍然良性的例常更新;marketplace 把 pin 推進到 bbb…bbb
  4. 變臉。 攻擊者建立名為 bbb…bbb 的分支,設為儲存庫預設分支,並指向惡意程式碼。被釘選的 commit 本身可以保持不動。
  5. 自動更新到 RCE。 變更後的 pin 觸發所有代理的背景自動更新 — Claude Code 與 Codex 的預設行為 — 檢出解析到該分支,程式碼在無點擊、無提示、無需重裝的情況下執行(AIR)。

替代路徑更快:接管合法維護者的儲存庫,直接跳過埋點步驟。AIR 的配套研究量化了其普遍性 — 925 個 skill 被從原始維護者處劫持,波及 134,000 個代理(AIR)。這是一部三部曲的第三幕:The Story of Skills 展示了一個惡意 skill 波及 26,000 個代理;MCPJacking 透過過期網域發現官方 marketplace 中 155 個可劫持的 MCP 伺服器,研究者註冊這些網域後,在每一個信任它們的代理上取得了遠端 prompt 執行能力(AIR)。Plugin4Shell 是產業為遏制這一切而構建的機制的邊界失效。爆炸半徑也大於代理本身:插件與 add-on 繼承執行代理的開發者的權限 — 本地原始碼、雲端憑證、SSH 金鑰、內部儲存庫與生產系統(Cyber Security News)。

廠商記分板:兩家已修復、一家未修復、一家棄用未修復

揭露時間線顯示協同揭露起了作用 — 以及它止步於何處:

時間 事件
2026 年 5 月 AIR 發現缺陷,對四個代理均有可用的 PoC
2026 年 6 月 對四家廠商的協同揭露
2026-06-17 Anthropic 確認在 Claude Code 2.1.179 中修復
2026-08-04 Google 確認不會發布修復 — Gemini CLI 已棄用;使用者被告知遷移到 Antigravity
2026-08-12 OpenAI 的 Codex 0.146.0 確認已修復
2026-09-17 公開揭露

截至 9 月 18–19 日,記分板未變:Anthropic 已修復、OpenAI 已修復、Microsoft 尚未發布 Copilot 修復、Google 棄用 Gemini CLI 且未提供修補 — 意味著所有現有 Gemini CLI 安裝將持續暴露(Help Net Security)。GitHub 的應對 — 在其平台上禁用 SHA 形狀的分支與標籤名 — 並未堵住缺口,因為 marketplace 可以託管在 Bitbucket 或自託管 git 伺服器上,這些名稱在那裡仍然合法(Cyber Security News)。

AIR 的總結最為誠實:"The fix has to ship in the agent, and updating is the only complete mitigation where one exists"(AIR)。其結構性原因值得為採購對話重述:pin 在代理內部解析,因此 marketplace 無法強制執行它所宣傳的保證。廠商側的 marketplace 上傳掃描(Anthropic 於 2026 年 8 月 6 日上線 Skill/Plugin Scanning)降低了惡意插件進入 marketplace 的機率,但無法替代代理側的檢出驗證 — 交換發生在審查之後、解析之時(MCP Security Hardening Checklist)。這也是治理層面的展示:四家廠商、一個共同的設計缺陷、一場持續三天的非對稱回應 — 採購組織可以把它當作一家廠商安全補丁節奏的縮影來解讀。

檢出後驗證:可泛化的控制

AIR 的一行修復可堵住兩種變體(AIR):

test "$(git rev-parse HEAD)" = "" || abort

細節承載教訓。git rev-parse HEAD 解析出工作樹真正包含的內容 — 而非請求的名稱。這一區分正是 Gemini FETCH_HEAD 變體得以溜過的地方。檢查必須在代理內部執行,因為 pin 在客戶端解析。一個在檢出後驗證的 marketplace 只是在驗證自己的記錄;必須在解析不一致時中止的元件是代理。

這一模式 — 在解析之後而非之前驗證 — 可泛化到代理技術堆疊中每一個釘選構件的控制。釘選的 MCP 伺服器版本、釘選的 skill、釘選的模型權重、釘選的容器 digest:每一個都是「信任某個安裝器會兌現、卻無人驗證其解析結果」的主張。同樣的缺失斷言存在於檢出發生的每一處。本週需要落實的四項:

  1. 更新已有修復的代理。 Claude Code 升級到 2.1.179 或更高;Codex 升級到 0.146.0 或更高。Copilot 與 Gemini CLI 沒有修復 — 在補丁發布前限制這些代理的可達範圍(檔案系統、憑證、網路出口),或遵循廠商的遷移路徑。
  2. 盤點每一個釘選構件。 插件、skill、MCP 伺服器版本、內部安裝器。逐一確認是否存在略過「解析後 HEAD」斷言的程式碼路徑 — 這包括團隊自己寫的內部工具,不只是廠商代理。
  3. 稽核插件儲存庫的劫持信號。 意外的分支變更、預設分支移動、所有權轉移。925 個被劫持的 skill 在本次揭露之前就已被奪走;儲存庫劫持是入口步驟,而且已經在規模化運作(Cyber Security News)。
  4. 把插件自動更新當作供應鏈交付通道,而非便利功能。 將代理可拉取的 marketplace 與儲存庫列入允許清單;在代理配置允許處,把重新釘選置於重新審查之下。零點擊屬性來自自動更新 — 去掉「零」,攻擊就需要使用者動作。

兩個相鄰 CVE,以及持續暴露

同一週還出現了兩個 MCP 伺服器 CVE,與 Plugin4Shell 主題相同 — 信任被置於一個從未驗證呼叫方的元件上。CVE-2026-54618 影響 0.2.0 之前的 Obsidian Web MCP:OAuth 授權端點向任何呼叫方發放授權碼而不驗證使用者,使未經身分驗證的遠端攻擊者獲得對整個 vault 的完整讀、寫、搜尋、移動、刪除存取權限,已在 0.2.0 修復(Rapid7;GitHub advisory GHSA-hwhg-mrjc-8g43)。CVE-2026-54446 是 NetLicensing-MCP 中的缺失身分驗證缺陷(CWE-306)(Practical DevSecOps)。它們疊加在數月未動的生態基線數字上:MCP 月下載量 97M+,抽樣伺服器中 82% 存在路徑遍歷漏洞,僅 8.5% 使用 OAuth(Practical DevSecOps)。

三項披露的規律在代理技術堆疊的每一層相同:某個信任機制 — 一個 pin、一個 OAuth 流程、一個伺服器端點 — 在錯誤的時間執行檢查,或根本沒有執行。Plugin4Shell 只是第一個一次性從單一產品跨越到整個生態的案例。

相關閱讀


一家中型工業分銷商為內部工具執行 Claude Code,並部署了一個對 NetSuite 和兩個供應商目錄報價的採購代理。團隊清點了兩個面上的每個釘選構件,把 Claude Code 升級到 2.1.179,停用背景插件自動更新,並在自己的內部 MCP 模組安裝器中加入解析後 HEAD 斷言。插件來源移入兩個已審查儲存庫的允許清單,稽核軌跡記錄每次重新釘選及其 diff。下一次 marketplace 事件變成一次版本升級和一次審查,而不是一次事件回應。

申請範圍明確的構建。 一週 discovery。你將獲得系統清單、工作流地圖和固定範圍 — 無論是否與我們合作構建。

想為您的系統建構這個嗎?

這裡的每份文件都來自真實的生產工作。如果您有目標系統和工作流程想法,我們可以在一週內確定範圍。

申請客製開發

為期一週的發掘階段。您會拿到系統清單、工作流程地圖和固定範圍——無論您最終是否與我們合作開發。