返回所有文章
發佈於 · 作者 Renaud Deraison

git status 執行了他們的程式碼

2026 年 9 月 2 日,Manifold Security 揭露了 GitSpawn:橫跨七款命令列編碼代理的八個缺陷,儲存庫自己的 .git/config 就能在開發者的機器上執行一道指令。代理跑 git status 想知道自己在哪個分支,git 順手更新索引,於是儲存庫選定的指令就以登入使用者的權限執行,在沙箱之外,早於信任提示,而在其中一款工具上甚至早於登入。沒有提示詞,沒有工具呼叫,沒有模型回合。Bromure Agentic Coding 不去修補這條路徑,而是換掉這條路徑通往的那台機器。

你什麼都沒打,也什麼都沒批准。代理還沒啟動完,而在其中一款工具上,你甚至還沒 登入。某個人寄給你的資料夾以你的身分執行了一道指令,而執行它的正是 git。

一位外包完成了一段工作,把專案目錄的 zip 放進共用資料夾寄給你。你解壓縮、cd 進去, 啟動編碼代理來熟悉一下狀況。代理開頭就問 git:這是哪個分支、哪些檔案有改動。整個攻擊 就這樣結束了。

2026 年 9 月 2 日,The Hacker News 報導了 Manifold Security 的一則揭露, 內容涵蓋橫跨七款命令列編碼代理的八個缺陷,以 GitSpawn 之名發表。這些受影響的工具加起來 接近五十萬顆 GitHub 星星,光是 Claude Code 就 每月超過 7,700 萬次 npm 下載。 八個之中有四個,在 Manifold 於 9 月 1 日重新測試時仍會執行儲存庫提供的指令。

一個值就是指令的效能設定

core.fsmonitor 是 git 給超大型儲存庫使用者的選項。與其走遍整棵樹來找出哪裡變了,git 可以呼叫一支外部的輔助程式去問。這個設定的值本身就是那道指令。git 從儲存庫自己的 .git/config 讀出它,並在每次更新索引時執行,也就是在 git status、在 git diff、在 每個工具都倚賴的底層管線上執行。

Manifold 檢視過的代理幾乎全都在啟動時跑這些指令來蒐集專案脈絡,而它們沒有一個會先看一眼 .git/config。於是儲存庫指名指令,git 照跑不誤。用研究人員的話說,它是「以登入使用者的 完整權限,在沙箱之外」執行。OpenAI 在自家針對 Codex 版本的公告裡也說,這支輔助程式 「在 Codex 的指令沙箱之外執行,而且沒有使用者核准提示」。

git clone 完全不會帶上這些。複製傳輸的是物件與 ref,來源儲存庫的本機設定留在原地。 所以被下毒的專案必須以檔案的形式、連同完好的 .git 目錄送到你手上:一份 zip 壓縮 檔、一個共用磁碟、一個同步資料夾、一支 USB 隨身碟。Manifold 列出的正是顧問、客戶與同事 交接工作的方式,也是唯一一條完全沒有 pull request 的送達路徑。git 自己防範敵意儲存庫的 機制 safe.directory 檢查的是這個儲存庫是不是別人的。是你解壓縮的,所以它是你的,於是 這道防線無話可說。

時序讓每款代理自己的保護都晚了一步。在 Claude Code 與 Hermes Agent 上,酬載在工作區 信任提示之前觸發,而那個對話框存在的目的正是問你信不信任這個目錄。在 Qwen Code 上, 它在使用者完成認證之前觸發;在 Grok Build 上,它在第一次按鍵時就觸發。

GitSpawn — 從交付到 shell資料夾送到zip、共用磁碟、同步資料夾、USB —.git/config 完好代理開始啟動蒐集脈絡:git status · git diffgit 更新它的索引讀取儲存庫自己的設定core.fsmonitor = …<他們的指令>你的使用者、你的環境、你的家目錄還沒發生的事信任提示Claude Code,Hermes Agent登入Qwen Code 在認證前就先觸發打任何一個字Grok Build 在第一次按鍵時就觸發模型沒有工具呼叫、沒有回合、沒有東西可記上排每一道關卡都是代理啟動流程裡的一步。指令在流程走到它們之前就已經跑完了。
整條鏈,以及它跑完時還沒發生的四件事。儲存庫的 .git/config 在 core.fsmonitor 裡指名一道指令;代理跑 git status 想知道自己在哪個分支;git 更新索引並呼叫那支輔助程式。它以登入使用者的身分執行,在 Claude Code 與 Hermes Agent 上早於信任提示,在 Qwen Code 上早於認證,在 Grok Build 上則在第一次按鍵時,而這整段流程裡沒有任何工具呼叫,也沒有任何模型回合。

八項發現、七款代理,其中四項仍未關閉

一半已經修好。goose 1.44.0 帶著 CVE-2026-72718(CVSS 7.0)的修補。 OpenAI 為 CVE-2026-19592 推出了 Codex CLI 0.131.0 與 26.519.x 的桌面版建置。Cursor 也 修補了自己的變體。Manifold 在 2.1.193 上確認了 Claude Code 的 core.fsmonitor 路徑, Anthropic 在 2.1.196 關閉了它,但沒有發佈公告:Manifold 指出,關於 Claude Code 的這兩項 發現都沒有出現在 npm 的公告紀錄裡。

另外一半原地不動。Hermes Agent 有一個 CVE(CVE-2026-71963),以及一份在六次聯繫之後仍 未被分類處理的私有公告。Qwen Code 的維護者在 7 月 7 日接受了回報,而 0.22.3 仍在執行那道 指令。Grok Build 也仍在執行。而 Claude Code 還有第二條路徑,走的是它的 ultrareview 指令,濫用另一個 git 設定鍵,在 2.1.252 上依然活著。Manifold 選擇不公開那個鍵的名稱, 以免發出一份可重複使用的攻擊範本。

Anthropic 三天內修好了第一條路徑。這一類缺陷則從同一個產品、同一個檔案裡的另一個鍵繞了 過去。

這是 git 的執行面,由每一個會呼叫 git 的工具繼承下來,而 git 不只一個設定的值是它會替你 執行的指令。分頁器、編輯器、SSH 指令、hook 路徑、diff 與 filter driver,以及以 shell 跳脫字元開頭的別名,全都接受指令值。現在每一家廠商都得逐鍵釐清,其中哪些是儲存庫從啟動 路徑就構得著的,而對手是一份會隨時間長出新鍵的設定綱要。Manifold 建議的修法,也就是在 背景脈絡操作期間清理 git 設定並停用 core.fsmonitor,本質上是一份要拿來對抗整個命名 空間的拒絕清單。研究人員在比報告點名還多的代理裡找到了同樣的模式。

本來能攔下這件事的控制,都在它的下游

把一支謹慎的團隊今天架在編碼代理上的東西排開來看:工具使用時的權限提示、工作區信任、 在指令執行前檢查它的 hook、把工作階段送往合規後端的記錄端點,以及用於權限決策與繞過的 OpenTelemetry。

這些全都錨定在代理自己的迴圈上,也就是錨定在代理決定發出的某次工具呼叫,或模型走過的 某個回合上。GitSpawn 兩者都不產生。執行酬載的行程是 git,是代理為了自己整理環境而 呼叫的,而指令來自一個代理從來沒讀過的檔案。沒有工具呼叫可以批准,也沒有 hook 會發火。 一支看著代理記錄落進自家 SIEM 的團隊,只會看到一段正常開始、什麼都沒顯示的工作階段, 正是我們在 Anthropic 推出 Compliance API 時寫過的同一個 盲點:一份錨定在「什麼抵達了模型」的紀錄,看不見在模型被諮詢之前跑過了什麼。

那道指令一定會跑。值得回答的問題是:它會找到什麼。

解壓縮到你的家目錄git status → core.fsmonitor 輔助程式由資料夾指定,以你的身分執行只隔一個目錄的東西~/.ssh/id_ed25519ANTHROPIC_API_KEY, AWS_SECRET_ACCESS_KEY~/.git-credentials, ~/.aws, ~/.kube/config~/.zshrc — 下次登入時的持久化對外:一個普通開發工具的普通連線,走普通的埠代理自己的紀錄裡沒有任何一列資料夾不受信任。它落地的那個帳號受信任。在 Bromure 工作區裡開啟git status → core.fsmonitor 輔助程式照樣跑 — 以 ubuntu 身分,在客體內拋棄式 Linux VM · 早於資料夾就開機沒有私鑰位元組 — 由主機 ssh-agent 簽章env 與設定裡的憑證全是誘餌 (brm_…):80 與 :443 導入主機代理,無法關閉對外防火牆:預設拒絕 → 沒有第二階段誘餌離開它的範圍 → 451,VM 暫停,工作區被標記,要抹除才能再開機每個請求都有一列,不管是哪個行程發的指令照樣跑。只是跑在一個為它而建的地方。
同一個資料夾、同一個 git status,兩台不同的機器。在你的筆電上,輔助程式以你的身分執行,離你的 SSH 金鑰與雲端憑證只有一個目錄的距離,而它的對外連線看起來就是一個普通開發工具的普通連線。在 Bromure 工作區裡,它以 ubuntu 的身分,在一台早於這個資料夾存在就開機的 VM 中執行:沒有私鑰位元組可偷,憑證該在的地方擺的是誘餌,而每一個出去的位元組都走在主機擁有並記錄的線路上。

讓它跑,看看它找到什麼

Bromure Agentic Coding 為每個工作區配一台跑在 Apple hypervisor 上的 專屬 Ubuntu VM。代理、它的 git、它的套件管理器,以及這三者啟動的任何東西,全都住在客體 裡。把外包的 zip 丟進一個工作區,讓輔助程式觸發,並且把一切都給攻擊者:信任之前、認證 之前,你想的話連客體裡的 root 都給。

讀取 SSH 金鑰。 沒有金鑰可讀。工作區的私鑰留在你的 Mac 上;VM 拿到的是一個透過 vsock 橋接到主機端代理的 SSH_AUTH_SOCK。那個協定有一種請求叫「替我簽這個挑戰」,卻 沒有任何一種請求叫「把金鑰給我」。

掃過環境找憑證。 憑證全都在,形狀也符合驗證器的期待,而且全都是誘餌。 ANTHROPIC_API_KEY 裡放的是假的 sk-ant-api03-brm-…~/.git-credentials~/.aws/config~/.kube/config~/.docker/config.json 也一樣。你真正的值加密存在 你的 Mac 上,而代理伺服器是在請求離開 VM 之後、且只有在它要去的正是該憑證所鑄給的那台 主機時,才把真值換到線路上。沒有任何開關能關掉這件事。這就是憑證在這個應用程式裡運作的 方式。

拉下第二階段。 那支輔助程式是個載入器,幾百個位元組,工作就是去把真正的酬載抓回來。 這次抓取會撞上工作區的對外防火牆。把未匹配流量設成 Deny,VM 就只到得了你列出的主機, 其他一概不通,而且在兩個地方強制執行:虛擬交換器以目的地 IP 與從 DNS 嗅到的主機名稱在 所有協定上比對,代理伺服器再以 TLS 伺服器名稱比對一次,對 web 規則還會比對 HTTP 方法。 如果載入器改去伸手向套件登錄檔,它遇到的是供應鏈管線,那裡的年齡門檻會拒絕比截止日更新 的版本。昨天才發佈的酬載過不了這一關。

把找到的東西送出去。 到這裡,總得有東西越過一條你的 Mac 擁有的線路。代理伺服器會用 一個 Aho-Corasick 自動機,把每一個對外請求的標頭與內文比對這個工作區鑄出的誘餌。一個 誘餌被送往它鑄造範圍之外的主機,正是一台機器正在送出它不該知道的東西的簽名。代理伺服器 會在任何一個位元組抵達目的地之前以 HTTP 451 擋下,暫停 VM,並發出一則點名該憑證與兩台 主機的警示。接著 Bromure 會把這個工作區標記為已遭入侵,在你抹掉它的磁碟與家目錄之前, 它不會再開機。

留下持久化。 客體裡被改過的 .bashrc 只能活到你讓這台 VM 活到什麼時候。抹除家 目錄會重設 /home/ubuntu重設磁碟會從簽章過的基礎映像重新複製系統磁碟,而 bromure-cli vm run --rm 給你的工作區會在 VM 停止時自我刪除。

這個攔截沒有任何環境變數可以取消

一個要客體自己選擇加入的代理伺服器,就是客體可以拒絕的代理伺服器。Bromure 的虛擬交換 器直接把 VM 的 80 埠與 443 埠流量導進主機的代理伺服器:沒有 HTTPS_PROXY 可以取消, 客體裡沒有任何東西關得掉它,而純 HTTP 也和 HTTPS 一樣被檢查。這裡發出請求的那個行程 從沒讀過代理的設定,它也不是代理。它是一支 git 啟動的輔助執行檔,而它走的是跟其他所有 東西一樣的那條線。

就算沒有任何工具呼叫,那一列還是存在

主機的代理伺服器會為每一個離開 VM 的請求寫下一筆中繼資料紀錄,不論是哪個行程發的: 時間、主機、埠、方法、路徑、狀態、延遲,以及在換上真憑證之前量到的位元組數。防火牆 判定、供應鏈決策與憑證代管都會落進安全時間軸(視窗 → 安全時間軸…),在已納管的 Mac 上,它們還會以 egress.firewallsupply_chain.fetchcredential.exfiltration 送進組織的事件串流。這場攻擊在代理端的記錄是空白的,而線路 端的紀錄正載著那個請求。

客體只構得到你 Mac 上的一樣東西。共享資料夾是一扇通往你真實檔案系統、活生生且可讀寫的 窗,而入侵後的抹除依設計不會碰它。分享專案目錄而不是你的家目錄,那麼輔助程式那句找 *.pemfind,走過的會是一個塞滿 checkout 與套件快取的 Linux 家目錄,而不是你那些 靠鑰匙圈撐著的登入資訊。

那條不在流程裡的邊界

把 GitSpawn 的四個時序細節放在一起讀——早於信任提示、早於認證、早於第一次按鍵、早於任何 模型回合——它們說的是同一件事。每一道保護都是啟動流程裡的一步,而每個新功能都會在它前面 再插一步。Cursor 在 2025 年因為儲存庫提供的 mcp.json 學過一次,今年 7 月又因為一道 早於自家信任提示執行的 worktree 設定指令再學一次。 現在輪到 git 了,一次七個產品,走的是一個在 git 裡已經存在多年的設定鍵。

hypervisor 不是那個流程裡的一步。它不讀 .git/config。沒有任何重構能把它重新排序,也 沒有任何程式碼路徑能豁免於它,因為它根本不跑在和那個提示同一支程式裡。zip 還沒下載完, VM 就已經在跑了,而它的兩項保證——這裡面沒有真正的機密,以及沒有未經中介的對外路徑——在 2.1.193 和在 2.1.196 一樣成立。

在打開別人寄給你的資料夾之前,先看一眼你的 .git/config。Manifold 這樣說是對的。你大概 也會照做兩個星期,然後在某個忙碌下午的第三個 zip 上跳過,而那差不多就是大多數以「打開 之前」收尾的指示的保存期限。 安裝 Bromure Agentic Coding,給那個陌生的資料夾一台屬於它自己的機器, 讓那份設定在一個「跑」是它唯一能做的事的房間裡,愛跑什麼就跑什麼。