你信任的是工作區,不是那些伺服器
2026 年 6 月 26 日,Wiz 揭露了 CVE-2026-12957:一個被 clone 下來的儲存庫裡的 `.amazonq/mcp.json` 會讓 Amazon Q 自動啟動 MCP 伺服器,而這些伺服器繼承了開發者完整的環境——AWS 金鑰、雲端 CLI token、API 機密、以及 SSH agent socket——伺服器本身完全沒有獨立的同意步驟。一次「信任這個工作區」的點擊,就頂替了去產生那些背景行程的決定,而它們帶著你正在使用的雲端工作階段。Amazon 在 Language Servers 1.69.0 修補了它。這裡說明為什麼這個修補關上了一個產品,卻沒有關上這整類問題,以及當開啟儲存庫的代理跑在按設定檔隔離的 Bromure VM 裡、藏在一個憑證仲介、一道讀寫護欄、以及一段 hypervisor 層級的軌跡之後時,什麼會改變。
昨天 Wiz 揭露了 CVE-2026-12957。一個你 clone 下來的儲存庫可以
夾帶一個叫 .amazonq/mcp.json 的檔案,而當你在 Amazon Q 裡開啟
那個資料夾、並點下 信任這個工作區 的那一刻,那個檔案就會啟動
背景伺服器,不再有第二個提示。它們起來時繼承了你的 AWS 金鑰、
你的雲端 CLI token、你的 API 機密、以及你的 SSH agent socket。
一次信任點擊就觸及了你的 shell 所能觸及的一切,並把它交給了
儲存庫所選擇的程式碼。
一個為了試用某個基礎設施模組而 clone 儲存庫的開發者,並不會
覺得自己授予了任何存取權限。clone 是惰性的:它把檔案寫到磁碟上,
不執行任何東西。需要判斷的時刻發生在後一步,也就是當你在編輯器
裡開啟那個資料夾、而助手問你是否要信任它的時候。那個提示讀起來
就像每個 IDE 都會顯示的、那道熟悉的「你是否信任這個資料夾裡檔案
的作者」關卡,於是你像過去點過上千次那樣點了「是」,因為另一個
選擇是一個讀不到你程式碼的助手。但這一次的「是」做的不只是讓
助手讀取。在 Amazon Q 裡,在修補之前,它同時也啟動了儲存庫寫進
.amazonq/mcp.json 裡的任何伺服器,而那些伺服器起來時,它們的
環境裡已經帶著你正在使用的雲端工作階段。
我們在這個月稍早 寫過同樣的形狀,當時 Miasma 蠕蟲把專案組態植進了 Microsoft 自家 73 個儲存庫,而酬載 在開啟資料夾時觸發。那個故事講的是一個 受信任的來源 成了攻擊 面。這一個更狹窄、也更令人不安:沒有蠕蟲、沒有被攻陷的維護者、 除了助手本身之外無物可指。這個漏洞出在 Amazon Q 如何讀取一個 尋常的專案組態檔,並決定在你那唯一一次信任點擊上,去產生帶著你 筆電上最敏感之物的長駐行程。
CVE-2026-12957 實際上做了什麼。
2026 年 6 月 26 日,Wiz Research 揭露了 CVE-2026-12957, 這是 Language Servers for AWS 裡的一個高嚴重性漏洞 (CVSS 8.5),而這個引擎驅動著 VS Code、JetBrains、Eclipse 與 Visual Studio 上的 Amazon Q Developer。Wiz 在 4 月 20 日 向 Amazon 回報,Amazon 在 5 月 12 日 推出了修補,細節在 6 月 26 日 公開。沒有跡象顯示它曾在野外被利用。剝到只剩 機制的樣貌:
- Amazon Q 會從你開啟的工作區讀取
.amazonq/mcp.json:一個 專案範圍的檔案,宣告了供助手使用的 Model Context Protocol 伺服器。 - 在開啟資料夾時,Q 會 啟動那個檔案所宣告的 MCP 伺服器,而 伺服器本身沒有獨立的同意步驟。因此一個你 clone 下來的儲存庫, 可以把伺服器定義擺到助手面前並讓它跑起來。
- 被產生的行程 繼承了開發者完整的環境,用 Wiz 的話說,就是 「AWS 金鑰、雲端 CLI token、API 機密、以及 SSH agent socket」。 一個 MCP 伺服器只不過是 Q 啟動的一個命令,而那個命令起來時 帶著你自己的 shell 會有的一切。
- 伺服器定義可以指向任何命令,因此這個檔案等同於在開啟資料夾時 任意執行程式碼:它以你的身分執行,你的雲端憑證已經載入,就在 你信任這個工作區的那一刻。
這次揭露帶著一個關於同意的真正分歧,而那正是這篇文章的全部重點。 Amazon 的立場:「使用者在被提示時必須信任這個工作區。」 是有 一道關卡,而你點過去了。Wiz 的發現:在修補之前,MCP 伺服器本身 並沒有獨立的同意步驟。兩者都成立,而失敗就活在它們之間的縫隙裡。 一個粗糙、熟悉的信任決定——和你為了讓編輯器索引一個資料夾而給出 的那個一模一樣的「是/否」——授權了一個大得多的給予:產生帶著你 正在使用的工作階段的背景伺服器。你回答的是一個關於 讀取檔案 的 問題。Amazon Q 卻把這次點擊花在了 以你的身分執行伺服器 上。
Amazon 在 Language Servers 1.69.0(用戶端:VS Code 2.20+、 JetBrains 4.3+、Eclipse 2.7.4+、Visual Studio 1.94.0.0+)裡修了它, 加上了原本缺失的每伺服器同意,並修補了一個同類的 symlink 檢查 繞過漏洞,CVE-2026-12958。更新 Amazon Q。然後越過這個修補, 看向它沒能解決的那一部分:開啟一個儲存庫,可以把你正在使用的 雲端工作階段放進一個由儲存庫所選擇的行程裡。
同一個儲存庫,在 Bromure Agentic Coding 裡開啟。
Bromure Agentic Coding 把你的編碼代理——包括
Amazon Q——跑在一個 按設定檔隔離的 Linux VM 裡:它自己的核心、
它自己的檔案系統、它自己的網路堆疊,建立在 Apple 的 Virtualization
框架上。一個設定檔就是一個連貫的工作範圍:這個客戶、這個服務、
這個你 clone 下來評估的開源模組。你把儲存庫 clone 進那個設定檔
並在那裡開啟它。漏洞行為會完全照 Wiz 所描述的那樣重現:你點下
信任這個工作區,Q 讀取 .amazonq/mcp.json,然後它啟動那個檔案
所宣告的伺服器。產生動作觸發了,儲存庫所選擇的程式碼跑了起來。
它跑在 guest 裡。MCP 伺服器在 VM 內部起來並繼承 VM 的環境,而 VM
的環境裡並沒有你的雲端工作階段。Bromure 在 guest 裡裝的是
stub:對 aws、gcloud、az、kubectl、git、以及任何
其他會讀取 Authorization 標頭或 AWS_ACCESS_KEY_ID 的東西來說,
看起來都像是真的假值。你 Mac 上的一個 proxy 守在每一條離開沙箱的
連線前面,認出那個 stub,並在請求離開時於 線路上 把它換成真正的
機密;那個握著金鑰的沙箱
詳述了這個機制。真正的 AWS 金鑰、真正的雲端 CLI token、真正的
API 機密,從不碰到 VM 能讀取的任何檔案、環境變數或記憶體分頁。
那麼就把這個 CVE 的繼承機制放到那道邊界上走一遍。被產生的伺服器
讀取 $AWS_ACCESS_KEY_ID,找到的是一個 stub。它讀取 ~/.aws,
找到的是一個 stub 設定檔。它向環境讀取雲端 token 與 API 機密,
找到的是佔位值。伺服器照所寫的跑到結束,繼承的是一個從未握著你
金鑰的盒子。讓 CVE-2026-12957 成為憑證竊取漏洞的,是 產生時的
環境繼承,而 Bromure 並不去破壞它。它讓被繼承的環境變得一文不值。
「信任這個工作區」問錯了問題。
這個 CVE 值得的不只是一則修補說明,原因在於那個同意縫隙,而那個 縫隙是一個粒度問題。信任提示問了一個寬泛的問題,你是否信任這個 資料夾,而一個「是」必須同時涵蓋你本意的那件無害之事(讓助手讀 我的程式碼)以及你不知道被附帶上的那件危險之事(用我的雲端工作 階段啟動背景伺服器)。你沒辦法好好回答它。沒有人能,因為這個問題 把兩個彼此毫無關係的給予熔在了一起,卻只給你看那個無辜的。
Bromure 不要求你把那個判斷做得更好。它把那個判斷從路徑上拿掉。 在一個設定檔內的信任點擊,仍然讓助手做它的工作,而它仍然無法 啟動一個握著你真正雲端工作階段的行程,因為那個工作階段並不在 VM 裡可供繼承。它活在主機上、藏在仲介之後,只能以一個被 stub 化 的請求觸及,由 proxy 在線路上填入並記錄。那個給予裡危險的那一半 不見了。你並沒有去拒絕它;那裡根本沒有東西可以給予,因為邊界 就坐落在代理之下、信任提示動不到的地方。
那 另一 半危險的事,是一個由窺探轉為破壞的被產生伺服器,而它
撞上的是 護欄。Bromure 讀的是操作本身,不只是連線:一個
aws s3 ls 是讀取,一個 aws s3 rm 是寫入;一個 git fetch 是
讀取,一個 git push 是寫入。把一個設定檔的雲端憑證設成
寫入時詢問,那麼當程式碼伸手去做一個會改變狀態的呼叫——例如
一個 DELETE、一個 Terminate*、或一次強制推送——的那一刻,
Bromure 會在線路上把它擋下,並在你 Mac 上跳出一個指明動詞、目標
與設定檔的提示。讀取永遠不會打斷你。會暫停的是那個變更,就像
「代理刪掉了正式環境資料庫」不再是一份事後檢討
而變成一個你拒絕掉的對話框。
軌跡顯示了什麼。
每個團隊在這類事情落地的隔天早上都會問同一個問題:我有開過那個 儲存庫嗎,它跑了什麼? 在一台普通的筆電上,誠實的答案是一個聳肩。 Q 產生的那個 MCP 伺服器是你工作階段裡的一個子行程,而無論它讀了 什麼、撥到了哪裡,它都是以你的身分做的,沒有任何把它和你自己的 工作區分開來的紀錄。
在 Bromure 裡,代理坐在一個 VM 裡,而每一個對外送出的位元組都會 經過主機的 proxy,因此即使代理看不到,那次產生與它的呼叫仍然 是可見的。主機把 MCP 伺服器的起來、它跑的命令、它向仲介索取去填 的憑證、以及它嘗試過的每一條連線,都記錄進和代理做過的其他一切 相同的工作階段軌跡裡,寫在代理之下、被產生的 程式碼伸不進去編輯的地方。「這個儲存庫的伺服器有沒有碰過我的 帳號」不再是一個你幾週後從 CloudTrail 重建出來的猜測,而變成一行 你讀得到的紀錄:仲介遞出的是一個 stub、請求有一個名字、目標被 記錄了下來。對一家企業而言,那就是 我們修補了 Amazon Q 與 我們能展示我們開發者開過的每一個儲存庫做了什麼 之間的差距,而 這個稽核態勢,正是我們的企業工作一再回到的 那一個。
這裡無法救你的地方。
一個被轉送進去的 ssh-agent socket,在它被轉送期間就是可用的。
Bromure 把你的 SSH 私鑰留在 macOS Keychain 裡,從不把它們複製
進 VM。但如果一個設定檔有把 ssh-agent socket 轉送進去(照
OpenSSH 設計的方式),一個被產生的伺服器可以在那個 socket
還活著時,要求 agent 用那些金鑰去 簽署。金鑰檔從不離開主機;
但簽署的能力確實觸及了 guest。只把 socket 轉送進需要它的設定檔,
並像仲介為憑證劃定範圍那樣為它劃定範圍。
一個你核准的寫入,就是一個會發生的寫入。
讀寫護欄攔下的是代理沒告訴你的那個破壞性呼叫。它讀不出你心裡
的意圖。如果你是刻意要拆掉一個堆疊、而你核准了那個提示,
Bromure 會把那個 Terminate 轉送出去。提示換來的是你看一眼
動詞與目標的機會;你仍然得去讀它。
設定檔是長存的,所以持久化會持久下去。
一個 Bromure 設定檔不是一張用完即丟的磁碟。一個把自己寫進設定檔 內某個啟動路徑的被產生伺服器,可以在那個設定檔的下一個工作階段 裡醒來。它醒來時面對的是一個沒有主機金鑰的 guest,以及一個只說 短期、需提示、有範圍的 token 的仲介:身處一個空無一物的盒子裡, 但畢竟還是身處其中。
修補過,不等於解決了。
Amazon 在 1.69.0 裡加上了每伺服器同意,你也應該更新。但下一個 會在開啟資料夾時讀取專案組態的助手,仍會做出同一類的決定,而 它顯示的提示看起來會一樣稀鬆平常。隔離才是那個不仰賴某個特定 廠商把某個特定同意對話框做對的部分。
下一個工作區早已在某處被 clone 下來了。
Red Hat scope 的教訓是: 發行者不是一道防線。Microsoft 儲存庫 的教訓是:儲存庫也不是,而且開啟一個資料夾就足以執行程式碼。 Amazon Q 加上了下一行:信任提示也不是一道防線。它擺到你面前的 問題,你是否信任這個資料夾,並不是那個答案才重要的問題,而那個 問題是 這個資料夾是否應該能夠產生帶著我雲端工作階段的伺服器。 你從來沒被給看第二個問題,而就算你被給看了,你也答不好。
你不會靠更仔細地讀那個提示來修好它,因為那個提示本身就是錯的
提示。你修好它的方式,是把事情安排成讓「這是哪一個工作區」不再
是你雲端金鑰所懸繫的那個問題;為什麼一個編碼代理不是一個沙箱
是這個論點的較長版本。Bromure Agentic
Coding 就是這樣一種配置:代理在一個按設定檔
隔離的 VM 裡開啟儲存庫,真正的憑證留在主機上、藏在仲介之後,
代理做的每一次寫入都得先過一個提示,而它產生的每一個伺服器都
被寫進一段它無法編輯的軌跡裡。一個被下毒的 mcp.json 最糟也
只能繼承一個從未握著你金鑰的盒子。它是免費的、開源的,今天就
出貨了。