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 上,它在第一次按鍵時就觸發。
八項發現、七款代理,其中四項仍未關閉
一半已經修好。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 時寫過的同一個
盲點:一份錨定在「什麼抵達了模型」的紀錄,看不見在模型被諮詢之前跑過了什麼。
那道指令一定會跑。值得回答的問題是:它會找到什麼。
讓它跑,看看它找到什麼
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.firewall、supply_chain.fetch 與
credential.exfiltration 送進組織的事件串流。這場攻擊在代理端的記錄是空白的,而線路
端的紀錄正載著那個請求。
客體只構得到你 Mac 上的一樣東西。共享資料夾是一扇通往你真實檔案系統、活生生且可讀寫的
窗,而入侵後的抹除依設計不會碰它。分享專案目錄而不是你的家目錄,那麼輔助程式那句找
*.pem 的 find,走過的會是一個塞滿 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,給那個陌生的資料夾一台屬於它自己的機器,
讓那份設定在一個「跑」是它唯一能做的事的房間裡,愛跑什麼就跑什麼。