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

你信任的是工作區,不是那些伺服器

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。然後越過這個修補, 看向它沒能解決的那一部分:開啟一個儲存庫,可以把你正在使用的 雲端工作階段放進一個由儲存庫所選擇的行程裡。

開發者筆電 — 你正在使用的雲端工作階段就在每個被產生的伺服器所繼承的環境裡開發者 clonegit clone …/terraform-modules尚未執行任何東西夾帶 .amazonq/mcp.json磁碟上的一個伺服器定義在 AMAZON Q 裡開啟「信任這個工作區?」一次熟悉的點擊 → [Trust]讀取 .amazonq/mcp.json沒有每伺服器同意MCP 伺服器自動啟動Q 產生所宣告的命令命令 = 儲存庫寫的任何東西以你的身分、在你的 shell 裡執行↳ 開啟資料夾時的任意程式碼被繼承的環境 — 每個被產生的伺服器都拿到全部,全是真的$AWS_ACCESS_KEY_ID, ~/.aws你的帳號(真的)雲端 CLI token(gcloud, az)你的專案(真的)env / .env 裡的 API 機密正式環境金鑰(真的)$SSH_AUTH_SOCK以你的身分簽署(真的)讀取 · 使用 · 外洩→ 離開你的機器結果程式碼以你的身分執行雲端工作階段在手金鑰被讀取並送出一次信任點擊換來的是整台筆電
在一台普通的開發者筆電上的 CVE-2026-12957。clone 儲存庫不會執行任何東西。在 Amazon Q 裡開啟它並點下「信任這個工作區」這唯一一道熟悉的關卡,會讓 Q 讀取 .amazonq/mcp.json 並啟動它所宣告的 MCP 伺服器,沒有獨立的每伺服器同意。每個被產生的伺服器都是 Q 啟動的一個命令,而它起來時繼承了開發者完整的環境:AWS 金鑰、雲端 CLI token、API 機密、SSH agent socket。因為一個伺服器定義可以指向任何命令,這就是以你的身分執行的任意程式碼,而你正在使用的雲端工作階段已經載入。那唯一一次信任點擊回答的是一個關於讀取檔案的問題,而 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:對 awsgcloudazkubectlgit、以及任何 其他會讀取 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 並不去破壞它。它讓被繼承的環境變得一文不值。

按設定檔隔離的 BROMURE VM信任工作區 → Q 讀取 mcp.jsonMCP 伺服器自動啟動、執行任意程式碼 — 只在 guest 裡伺服器繼承了什麼$AWS_ACCESS_KEY_IDstub_AKIA…雲端 token、API 機密stub / 缺席$SSH_AUTH_SOCK已轉送嘗試:aws s3 rm / ec2 terminate一次寫入 → 經由 proxy 離開外洩主機金鑰:沒有真東西可拿爆炸半徑 = 這一個設定檔碰不到主機 fs、碰不到主機 keychainPROXY · 你的 MAC憑證仲介真正的 AWS 金鑰存在這裡stub → 真值,在線路上值從不進入 VM護欄:讀取/寫入刪除 / 終止 → 提示指明動詞 + 目標稽核每次產生 + 呼叫都記錄在代理之下結果雲端工作階段:繼承到 stub,真正的金鑰未被觸及以你身分執行的程式碼:跑在一個用完即丟的guest 裡,不在主機上破壞性呼叫:暫停,你說不跑了什麼:都在軌跡上
同樣的開啟資料夾、同樣的自動啟動伺服器,但發生在 Bromure 裡。(1)隔離:MCP 伺服器在按設定檔隔離的 VM 裡產生,因此「以你身分執行的任意程式碼」落在一個可丟棄的 guest 裡,而不是落在主機上,爆炸半徑就是一個設定檔。(2)憑證仲介:伺服器繼承 guest 的環境,而那裡只有 stub;真正的 AWS 金鑰、雲端 token 與 API 機密待在主機上、藏在 proxy 之後,在線路上換入,因此這次繼承拿到的是佔位值。(3)護欄:程式碼嘗試的任何會改變狀態的呼叫——例如刪除一個 bucket、終止一個執行個體、或推送一個分支——都是一次寫入,會在線路上被擋下,跳出一個指明動詞與目標的提示。每一層都是在代理之下、在被產生的行程繞不過去的邊界上強制執行,而每一次產生與每一次對外呼叫都被寫進軌跡裡。

「信任這個工作區」問錯了問題。

這個 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 最糟也 只能繼承一個從未握著你金鑰的盒子。它是免費的、開源的,今天就 出貨了。