登錄檔在沙箱之間傳紙條
Check Point 於 9 月 8 日發表研究,指出兩個 ChatGPT 的程式碼執行容器——分屬兩個不同帳號、彼此之間沒有網路路徑——可以透過那個為它們供應套件的內部 JFrog Artifactory 實例互傳資料。前門什麼都沒壞。那些容器持有的讀取憑證同時也能寫入,於是儲存套件上的一個中介資料欄位就成了信箱。Bromure Agentic Coding 不給訪客任何鏡像上的帳號,並且讓你把 read-only 寫進一條由線路強制執行的規則裡。
隔離守住了,連接器的行為與文件相符,而中間那個服務本來就是為了安全才裝上的。 受害者的信件依然到了陌生人的帳號裡,走的是儲存套件上的一個中介資料欄位。
六月,Check Point 的研究人員在一個套件儲存庫裡的檔案上寫了一個屬性。屬性名叫
chatgpt_test_ts,值是一個時間戳。他們是從執行 ChatGPT 對話程式碼的那個沙箱內部寫
進去的。接著他們用另一個帳號開了一段對話,跑在它自己的容器裡,把同一個屬性讀了回
來。值完好無損地穿過來了。
那個時間戳就是整個發現。 Check Point 在 9 月 8 日發表了它, 標題勝過任何摘要:The Shared Clipboard Inside the Sandbox。到那時 OpenAI 已經把該 服務退役。研究人員在六月下旬通報,得到的回覆是內部那個 Artifactory 實例已經下線,所 以沒有補丁要打,使用者也沒有事情要做。但它的形狀留了下來。
中間那個服務
會執行程式碼的 ChatGPT 對話會拿到自己的容器,與其他對話的容器隔離。這部分是有效的。
跑 Python 的容器也需要 pip install,而謹慎的平台團隊不會讓沙箱自己伸手去碰
pypi.org。於是沙箱得到一個受管控的中間人:一個內部的
JFrog Artifactory 實例,擋在公開登錄檔前面,把審核
過的套件往內送。
這是好設計。銀行會把同樣的東西擋在 npm 前面,受監管的醫療業者會把它擋在 PyPI 前面。 鏡像的存在是為了減少沙箱的曝露面,而它確實做到了。
它同時也給了每個容器一個鏡像上的帳號。
Artifactory 的 REST API 裡有一組 Item Management,位於
/api/storage/{repoKey}/{itemPath},讓用戶端可以在儲存項目上附加任意的具名字串屬
性,再讀回來:Set Item Properties 和 Get Storage Item Information。設定屬性
需要 Annotate 權限。讀取不需要。Check Point 發現,發給沙箱容器的那份憑證——本意是
唯讀的——兩者都帶著。如
Cybersecurity News 的總結
所說,「交給容器的讀取憑證,其權限足以同時寫入與讀取這些屬性」,而且「那些屬性並未
依帳號隔離」。
屬性值可以放文字,二進位的東西則放 Base64;一份塞不進單一屬性的載荷會切分到好幾個屬 性,在另一端重新組回來。兩個屬於毫無關聯客戶的容器,彼此之間沒有網路路徑,如今握著一 條頻寬上限不值一提的雙向通道。
同一輪裡的兩段對話
攻擊者的指令靠尋常途徑抵達受害者:一段貼上的提示、一個分享的對話連結,或是一個 使用者看不到其建構指令的自訂 GPT。接著受害者送出一則正常訊息,這就是全部的觸發條 件。
在 Thinking 模式下,模型在同一輪裡處理了兩道獨立的工作流。Check Point 的概念驗證讓受 害者問一個做菜的問題並得到正確答案,而在那個答案旁邊,模型讀了受害者已連接的 Gmail 帳號,把找到的東西寫進一個 Artifactory 屬性,等攻擊者的容器來收。 領導 Check Point 惡意程式分析團隊的 Pedro Drimel Neto 告訴 The Register: 「一段精心構造的指令,可以讓 ChatGPT 在可見的對話旁邊處理第二道任務流。」Gmail 只是 示範。Google Drive、Microsoft Teams 和 GitHub 都在同一個伸手可及的範圍內。
受害者看到的訊號只有一個,一個小小的 「Talked to Gmail」 標籤,而且是事後才出現 的。連接器預設是 Important actions 設定,會不問就核准低風險的讀取;較嚴格的 Always ask 才會彈出詢問。這個設定把一次讀信歸為低風險,卻不問是誰要求的。
Check Point 對模型的描述是:它「因此可以扮演一個被脅迫的內部人:並非惡意,而是被說服 去替攻擊者行事」。
可以一般化的那部分
把 ChatGPT 拿掉,這個故事講的就是一個編碼代理,因為結構一模一樣。
代理的沙箱必須安裝套件。讓它毫無中介地連上公開登錄檔,正是你原本要避免的事,所以你在
前面擺一個鏡像:Artifactory、Nexus、私有的 npm 代理,你們組織標準化成什麼都行。沙箱
現在要向那個鏡像驗證身分,於是它持有一份憑證,而那份憑證能做什麼,早在這個代理存在之
前就有人決定好了。套件登錄檔接受上傳、標籤、註記和保留規則,這就讓 Annotate 成了一
個小小的權限,坐在一片很寬的面上。
所以,別問沙箱有沒有隔離,改問你自己的環境兩件事。指出它在什麼東西上持有帳號。然後指 出哪些動詞在那個帳號上行得通。
在 Bromure 的工作區裡
Bromure Agentic Coding 把每個編碼代理跑在你 Mac 上一個硬體虛擬化的 Linux VM 裡,並把 真正的祕密留在那條界線的主機側。把 Check Point 的三樣材料放進工作區走一遍。
一個共用的服務。 沒有。每個工作區對應一個 VM,有自己的系統磁碟、自己的家目錄映 像、自己的 IP,以及主機上自己的 MITM 代理監聽器。兩個工作區不共用任何 VM,而手冊對其 中一對的描述是它們「從不共用一個位元組」。工作區底下沒有一個全機隊共用的鏡像,帶著兩 邊都能寫入的中介資料面。工作區底下坐著的,是你自己 Mac 上的一個行程,每個工作區一 個。
一份鏡像上的憑證。 代理一份也沒有。Bromure 工作區裡的套件安裝會經由一個 virtio
socket 出去到主機代理,由代理去跟登錄檔對話。Bromure 把信譽服務的 API 金鑰留在主機
側,不匯出到任何 VM。Depi 這條路徑是最鮮明的例子:代理在主機側把你的登錄檔金鑰當成
Authorization: Bearer 標頭附上,並在經過時剝除訪客送來的任何 Authorization 標
頭。訪客裡不存在讀取憑證,所以也沒有哪一份能多帶一個動詞。
一個活得比理由還久的寫入動詞。 這裡輪到你把約束寫下來。工作區的 Guardrails 面板
帶著一道有序的出向防火牆,而 web 協定的規則有一個 Methods 欄。在 allow 之
下,這些方法構成一份允許清單,而 read-only 是 GET、HEAD 和 OPTIONS 的簡寫。手冊說
明了這一欄為什麼存在:方法這個維度「就是讓工作區能夠讀取一個它無權寫入的 API 的
東西」,而且是在線路上,不管 VM 裡是哪個工具發出請求。有兩層在執行它。虛擬網路交換器
以目的 IP 和從 DNS 窺得的主機名稱比對每一條流,代理再以 TLS 伺服器名稱和個別 HTTP 方
法比對一次,所以被攻陷的代理繞不過任何一層。編輯不必重開機就會套到正在執行的工作階段
上,而每一次裁決都會落成 Security Timeline 裡的一列 Firewall。
第二道流,以及回到外面的路
攻擊者的第二道任務流,是以模型讀到的內容抵達的。在 Bromure 工作區裡,代理攝入並串流
回模型的一切(檔案內容、網頁、指令輸出、任何工具回傳的東西)都以 tool_result 區段
的形式穿過同一個主機代理。原始碼偵測器會在模型依據這些區段行動之前,用本機的
PromptGuard 分類器給它們打分,然後記錄命中、暫停等你決定,或是攔下請求。這個偵測器出
廠是關閉的,第一次啟用時會拉一個 298 MB 的模型,所以請在你需要它的那次工作階段之前先
啟用。
未經核准的連接器讀取,在這裡有它的反面。Bromure 工作區的 MCP bearer 權杖在 VM 裡是以
brm-mcp_ 佔位符的形式出現。真正的權杖留在訪客之外,而代理會在線路上,朝著那份權杖
唯一被鑄造給的那台主機替換上去。為某份憑證打開 Ask before use,它在一次工作階段
中的第一次使用就會暫停,彈出主機側的對話框,提供 5 分鐘、1 小時或本次工作階段剩餘時
間的授權。那就是「Important actions」拒絕顯示的那個詢問,逐份憑證,出現在你正坐著的
那台機器上。
萬一 VM 裡有什麼把佔位符帶到了不該去的地方,代理會一個位元組都不轉發地拒掉請求,暫停
VM,並發出一則警示,指名權杖的預覽、那份憑證、它被鑄造給的主機,以及它正要前往的主
機。在那條絆線之下,設在 Activity only 的追蹤會為每個請求寫一列中介資料:主機、
方法、路徑、狀態、位元組數、代理替換了哪些憑證,以及任何不是 Bromure 鑄造的 bearer
形式權杖的洩漏警告。工作階段結束後跑一下 bromure-cli trace hostnames,代理接觸過的
每一台不同主機都會連同次數印出來。
如果你為你的代理們營運一個鏡像
先查你的沙箱是用什麼身分驗證的,再談它們之間的網路路徑。在 Artifactory 上,去找一
找你以為是唯讀的權杖上有沒有 Annotate,以及有沒有比沙箱安裝來源更寬的
permission target。屬性 API 只是其中一個可寫入的中介資料面;保留規則、標籤和建置
資訊都是別的。一個能註記的讀者,就能張貼。
用一行把它答完的規則
在工作區的 Guardrails 面板裡,用 web 協定和 Methods 欄的 read-only 為登錄
檔加一條出向規則,然後把 Unmatched traffic 設為 Deny,再把工作需要的東西
列出來。面板底部的 pf 格式揭露會把你組出來的東西印出來:
allow web registry.npmjs.org read-only、allow web api.github.com、
default deny。它不必重啟就套用到正在執行的工作階段。
一個共用服務讓租戶變成鄰居
聽完這樣的故事,本能是去檢查牆。牆沒問題。兩個帳號底下的兩個容器彼此沒有路,Check Point 也沒找到。他們找到的是一個雙方都有權對話的第三方,並且用了人們用共用磁碟和 DNS TXT 紀錄用了三十年的那種方式去用它。
Check Point 在報告裡把教訓寫明了:「一個純粹作為基礎設施的共用內部服務,可能變成跨越 那些本該保持隔離的環境之間、非預期的通訊層。」你為了安全而裝的基礎設施仍然是基礎設 施,而它的租戶們就坐在一跳之外。縮小沙箱持有帳號的那組對象,並且把「這些帳號能做什 麼」這個決定,從兩年前某人結掉的一張開通單裡搬到每一個請求上。
值得拿來跑的代理沙箱,都會有一條通往它所需套件的受管控路徑,因為另一種做法更糟。把那 條路徑當成一段附帶權限的關係,並把那些權限當成你的邊界。在你還看得見它們的地方做決 定。 安裝 Bromure Agentic Coding,別給你的代理任何可以拿來登入的東西。