守衛與 shell 各說各話
6 月 30 日,Adversa AI 揭露了 GuardFall——一類繞過手法,讓數十年前的 bash 小把戲,直接從 11 款最熱門開源程式設計代理中 10 款內建的命令守衛前面走過去。原因很簡單,而且在那一層無法修補:守衛讀的是文字,而 bash 在執行前把文字改寫了。Bromure Agentic Coding 從未賭守衛能正確讀懂 bash。它賭的是——命令在執行的那一刻並不重要。
某個程式設計代理的安全過濾器看著字串 r''m -rf ~,看到一個它不認得的
命令,於是放行了。bash 看著同一個字串,丟掉空引號,執行了 rm -rf ~。
過濾器與 shell 讀的是兩種不同的語言。這道裂縫不是某個工具的錯誤——它是
「用模式比對代理所輸入的命令來守護代理」這整個念頭本身,而 Adversa AI
剛剛證明,它在受測的十一款代理中有十款失守。
命令守衛是每個代理式程式設計工具最先伸手去拿的功能。代理提出一個 shell
命令;過濾器檢查該字串;若看來具破壞性——rm -rf、以管線接的
curl … | sh、對裸裝置的 dd——守衛就攔下它或請你確認。它正是讓你敢打開
自動執行、覺得安全的那個東西。2026 年 6 月 30 日,Adversa
AI
發表了研究,The Hacker
News
與 SecurityWeek
都跟進報導,指出這項功能只是演戲。
GuardFall 是什麼
他們稱它為 GuardFall,而它不是一個 CVE。沒有東西可以修補,因為它不是某個 元件裡的瑕疵——而是其中兩個元件之間的錯配。用 Adversa 的話說:*「守衛檢查 原始文字,而系統 shell(bash)在執行前會展開、去引號並改寫文字。」*過濾器 比對的是模型吐出的字串。bash 接著讓那個字串經過去引號、變數展開與命令替換, 才會有任何一個位元組被執行。兩者從不看同一個命令,因此任何能撐過改寫、卻撐 不過比對的把戲,都能一路暢行。
這些把戲比大多數使用這些代理的人還老。Adversa 把它們歸成五類:
- 去引號。
r''m對正規表示式是兩個記號,對 bash 卻是單字rm,因為 它丟掉了空引號。 - 變數展開。
rm$IFS-rf$IFS/對模式比對器看來像一個長字;bash 展開內部 欄位分隔符,得到rm -rf /。 - 命令替換。
$(echo rm) -rf /把危險的動詞藏在$( )裡;檢查外層 字串的守衛從未看見rm。 - 編碼管線。
echo <base64> | base64 -d | sh——每個可見的記號都無害; 酬載只在解碼後才存在。 - 替代的破壞性旗標。 完全沒有
rm:find /x -delete、tar -C / -x、sed -i。一份圍繞rm與dd建立的黑名單永遠不會觸發。
Adversa 拿這些把戲對付十一款最常用的開源程式設計與電腦操作代理——其中包括
opencode、Goose、Cline、Roo-Code、Aider、Plandex、Open Interpreter、
OpenHands、SWE-agent 與 Hermes 專案,到五月時合計約 548,000 顆 GitHub
星。
十款倒下。唯一撐住的 Continue,撐住有個明確的理由:它不比對原始字串,而是在
決定之前,照 bash 將要做的方式把命令記號化——吸收引號、解析 $IFS、從替換中
取出內層命令、對任何以 sh 結尾的管線做標記。它為 shell 建模。那是守衛這一層
唯一有效的做法,是真實而細心的工程,如今其餘十款都得正確地重現它,並在 bash
的每一個邊界情況與每一次發行中維持正確。
問題出在那一層,不是正規表示式
誘惑在於把 GuardFall 讀成「十支團隊寫了弱過濾器」。那錯過了 Adversa 想說的。 他們最鋒利的一句,並非針對任何一款代理:
一個能在操作者主機上執行任意 shell 命令、僅靠一條比對 LLM 所吐字串的正規 表示式來把關的代理,並不是防禦。它在完全啟用且正確設定的狀態下失守,因為 字串比對無法為 bash 將執行什麼建模。
完全啟用且正確設定。 這不是你能關掉的錯誤設定。守衛坐在它永遠贏不了的那唯一 一層——在一個寫字串的模型與一個重新詮釋字串的 shell 之間——並要求一個文字比對器 去預測一個圖靈完備展開器的輸出。Continue 的「記號化並正規化」做法,藉由教守衛 像 bash 那樣思考來縮小裂縫,而如果你打算在那裡留一個守衛,這是正確的一步。 但那是一項逐代理、逐版本的承諾,要在解析上勝過一個花了三十年不斷累積改寫命令 手法的 shell。另外九款熱門代理,正說明了要弄錯有多容易。
Adversa 的補償性控制很說明問題。在任何結構性修補之前,他們要你把 $HOME
導向一個受限沙箱,保留專案存取但移除憑證,剝掉自動執行旗標,別讓代理在不可信的
拉取請求上執行,並把每個儲存庫設定檔都當成不可信的程式碼。再讀一遍那份清單。
那不是關於怎麼寫一個更好的過濾器的建議。那是關於怎麼在代理周圍造一個箱子的
建議——好讓過濾器輸的時候(他們正告訴你它會輸),漏過去的命令落在一個傷不了你的
地方。
那個箱子就是產品。
Bromure 從未要求守衛去讀 bash
Bromure Agentic Coding 從 GuardFall 所證明的假設出發:代理遲早會執行一個你
未曾核准的命令。它不試圖靠讀它來攔下那個命令。它把整個代理——Aider、Goose、
opencode,十款裡你喜歡哪個都行——放進一台可拋棄的 Linux VM 裡執行,並把每一個
網路請求都經由主機上的代理伺服器送出去。設計上的問題從不是「過濾器會認出這個
rm 嗎?」而是「當那個 rm 執行時,它能碰到什麼?」
把 GuardFall 自己的酬載放進那個箱子走一遍。
這個攻擊的重點,一旦一個藏起來的 r''m 或一個 find /x -delete 讓一份設了
陷阱的 README 溜過守衛,就是竊取這個帳號能碰到的東西——Adversa 點名 ~/.ssh、
~/.aws、雲端憑證、「任何躺在你家目錄裡的東西」——並把它抹除或外洩。在 Bromure
裡,那些是本就不在箱子裡的東西。真正的機密從不進入 VM:當你給一個設定檔一個
AWS_SECRET_ACCESS_KEY、一個 GitHub 權杖、一個 Anthropic 金鑰時,代理的環境拿到
的是一個 brm_… 佔位符,而主機代理伺服器只在送往應收取它的供應商的對外請求上,
於線路上把假的換成真值。Adversa 建議的緩解——把 $HOME 搬到某個「保留專案存取
但移除憑證」的地方——是 Bromure 設定檔預設就會做的事的手工、局部版本,對每一項
憑證,都不必你去寫腳本。
破壞性的那一半也一樣落地。一個溜過守衛的 find /x -delete 或 sed -i 確實
會執行——Bromure 不假裝它攔下了命令。它對著一個從共享基底複製而來的家目錄執行,
在一台你能從選單(而非事故表單)抹除並重置回基底的 VM 上。酬載可能植入的持久性,
死在那次重置。而且因為每個請求都經由主機代理離開,GuardFall 那類編碼管線本要藏起
的 curl … | base64 -d 信標並非隱形:它是安全記錄裡一行被記下、可歸屬的紀錄。
線路勝過 shell 的那唯一之處
Bromure 確實保有一個破壞性操作的守衛——它主機端的 Guardrails,我們曾在一個 Cursor 代理於九秒內刪掉一個正式資料庫 後寫過它。而在這裡,那一層對 Bromure 有利,因為 Guardrails 並不住在 GuardFall 住的地方。
GuardFall 是一種 shell 文字 攻擊。它的五類——去引號、$IFS、$( )、base64
管線、稀奇的旗標——每一類都是 bash 如何解析一個字串的把戲。Bromure 的 Guardrails
從不讀那個字串。它們坐在主機代理伺服器上,對代理向它所理解的供應商發出的結構化
API 呼叫進行分類——對 AWS、Kubernetes、git 平台、受管資料庫的實際 HTTPS 請求
——並對破壞性的那些在線路上回一個硬性的 403,就在 VM 裡被入侵的代理關不掉它的
地方。等到一個請求抵達那一層,它已經是一個被解析好的 DeleteDBInstance,而不是
一串引號與錢字號。沒有 shell 改寫可以夾帶任何東西,因為路徑上沒有 shell。
GuardFall 的整套技法都需要在檢查與行動之間有一個 bash;在 Guardrails 的路徑上,
沒有。
這劃出了哪條界線,把話說清楚
命令照樣執行
Bromure 是隔離,不是攔截。一個溜過你代理自身守衛的 GuardFall 酬載,會在 VM 裡執行。Bromure 改變的是災情範圍:一個可拋棄的家目錄、誘餌憑證、一條被記錄的 出口路徑。如果你需要命令本身被拒絕,那是你代理的守衛的工作——而 GuardFall 正是你不該倚賴它的理由。
本地破壞靠的是可拋棄性,不是阻擋
對 VM 內檔案的 find /x -delete 不是供應商 API 呼叫,所以 Guardrails 不去
把關它。對 VM 內破壞的答案,是 VM 可拋棄、而真正的工作住在一個掛載的儲存庫與
主機上,不是那個刪除被擋下。是重置回基底,不是回滾。
替換涵蓋你所設定的憑證
brm_… 的替換保護你放進設定檔的機密——模型金鑰、雲端與 git 權杖、受管資料庫
端點。一個你親手貼進箱內某檔案的密碼,或一個腳本在工作階段中途寫到磁碟的權杖,
就只是一個檔案。把機密放在代理中介裡,別放在工作區裡。
出口被記錄,並非預設禁止
外洩信標會出現在安全記錄裡;那是歸屬,不是防止。沒有任何真的東西離開,因為 憑證是誘餌,但如果你需要禁止 VM 與任意主機通訊,那是一條你仍要自行設定的網路 政策。
可以推而廣之的部分
GuardFall 是對一條整個類別一再重新學到的規則的清晰陳述:你無法靠要求一個過濾器 去預測它的命令將做什麼,來讓一個代理變安全。模型寫下一個字串,而在它底下某處, 一個 shell、一個 API 用戶端、一個套件管理器,或一個瀏覽器,用它自己的規則重新 詮釋那個字串。每一種檢查代理輸出、並指望它與最終行為相符的防禦,都是在對那道 裂縫下反向注,而裂縫總是贏——它在這裡贏了,在用 Adversa 的話說完全啟用且正確設定 的十一款代理中的十款上。
Bromure Agentic Coding 不下那個注。它假設命令會執行、守衛有時會漏、而你請進門的 代理可能被一份你從未寫過的 README 反過來對付你。所以它讓命令執行的那個地方變得 不值得攻擊:主機上的真金鑰、箱子裡的誘餌、一台可拋棄的 VM、一個留給沒有 shell 能改寫它的線路的硬性阻擋,以及一份所有試圖離開之物的紀錄。你可以執行 GuardFall 破掉的那十款代理中的任何一款——無論哪一款,結果都是同一個可拋棄的箱子。它是自由 且開源的。