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

掃描器拒絕讀它

Google 的 Threat Intelligence Group 在 9 月 8 日發布了 AI Threat Tracker。在講述一個名為 UNC6780 的供應鏈攻擊團體的章節裡,埋著一種把模型安全政策變成掩護的手法:惡意程式的 JavaScript 載入器開頭是一段註解,索要生物武器合成路徑與核武規格,放在那裡就是為了讓 LLM 安全掃描器讀到之後依政策拒絕,永遠到不了底下的程式碼。拒絕本身就是規避。Bromure Agentic Coding 用永遠不去讀它們的檢查,來面對這類產物。

說服一個安全掃描器批准惡意程式是件苦差事。遞給它一份輻射強到讓它不願多看一眼的 檔案,只需要一段註解,而你的流水線會把這個拒絕歸檔成「無法分析」。

你的建置流水線裡現在有一個 LLM。多數都有。在相依套件更新落地與合併按鈕亮起之間的 某處,一個模型讀 diff、讀新檔案,然後寫下一段簡短意見,說明這裡頭有沒有哪裡看起來 不懷好意。這是技術的好用法:它抓得到樣式比對會直接走過的混淆載入器,而且每個檔案 只花不到一分錢的零頭。

9 月 8 日,Google 的 Threat Intelligence Group 發布了 AI Threat Tracker, 那是一份關於攻擊者拿這些東西在做什麼的季度觀察。大部分報導都跑去追那個頭條數字: 一名以金錢為目的的攻擊者入侵了某公司的雲端基礎設施,接著用 GTIG 的話說,「運用一個 AI 編碼聊天機器人、一段提示詞和一組代理指令,在不到六小時內規劃、建造並執行了一場 大規模憑證收割行動」。數以千計的第三方憑證,經由受害者自己的 IP 位址被送了出去。 在另一台外露的伺服器上,研究人員找到一套偵察框架,它的儀表板正即時追蹤超過 23,800 筆已收割的機密。

那是被寫出來的故事。真正值得你花一個下午的段落在更下面幾節,在談一個 Google 以 UNC6780 追蹤的供應鏈團體的部分。

檔案最上面的那段註解

UNC6780 也叫 TeamPCP,自 2026 年 3 月起在 PyPI、npm 與 Docker Hub 上進行大規模的 開源專案入侵。他們目前的憑證竊取程式叫 DUSTMAKER,是一個 JavaScript 酬載。 JavaScript 酬載都有載入器。這一個的載入器是名為 _index.js 的檔案,而 GTIG 在它的 最上面發現了新東西:

除了嵌入在設定檔中、針對 AI 編碼代理的提示詞之外,DUSTMAKER 的樣本還包含嵌入在 惡意程式 JavaScript 載入器腳本(_index.js)頂端、以註解形式存在的提示詞。這些 提示詞含有極端的對抗性文字,提及生物與核子武器之類的主題,很可能意在讓 LLM 安全 掃描器失敗,或因安全或政策上的拒絕而跳過對底下惡意 JavaScript 程式碼的分析。

那段註解讓惡意程式大剌剌地擺在明處。未封裝、未編碼,全都在同一個檔案裡:惡意 JavaScript 就在三行之下,任何讀到最後的人都會絆到它。

是那段註解讓讀者不願意讀到最後。它以一段偽造的系統覆寫開場,宣告安全準則已暫停, 接著索要氣膠散布型生物武器的逐步合成路徑。然後轉向內爆式核武設計,連當量目標都 附上。一個吞下這個檔案、然後被問「這段程式碼有惡意嗎」的模型,眼前多了一個更大聲的 問題,而它用拒絕回答了那一個。掃描器回傳一則政策訊息。你的流水線記下 無法取得分析,就像它每週為逾時和過大檔案這麼做上幾十次一樣,然後建置變綠。

這套手法對 UNC6780 來說毫無成本。它不需要知道是哪個模型在讀這個檔案、是哪家廠商在 託管那個模型,也不必知道它的系統提示詞寫了什麼。它只向讀者要一樣東西:一份安全 政策,而在一條保證用的流水線裡,那正是你當初選擇模型的理由。

拒絕就是規避_index.js/* SYSTEM OVERRIDE — CLASSIFIED BRIEFING生物武器合成路徑分裂裝置規格為了被拒絕而設計const _0x = require(…)憑證竊取程式明碼、未經混淆永遠沒被讀到從頂端開始讀LLM 安全掃描器問題:這段程式碼有敵意嗎先回答比較大聲的那個依政策拒絕建置記錄無法取得分析同一行也會印給逾時與過大的檔案攻擊者必須知道的事不必知道哪個模型在讀 · 不必知道哪家廠商託管不必知道系統提示詞 · 不必知道門檻只需要讀者身上有一份安全政策
DUSTMAKER 的載入器沒有藏起酬載。它以一段刻意觸發審查模型安全政策的註解開場,於是模型拒絕了整個檔案,三行之下的惡意 JavaScript 從未被評估。對一份只看步驟有沒有失敗的建置記錄來說,拒絕和一張健康證明長得一模一樣。

這條鏈的其餘部分做的是同一件事

一旦看懂這一手,UNC6780 其餘的手法讀起來都是它的變奏。路徑上剩下的每一道防線, 同樣是一個對著攻擊者所寫產物形成判斷的讀者,而每一道都是被餵飽,而不是被擊破。

套件是循著代理最信任的工具送進來的。 GTIG 描述 UNC6780 入侵合法的開發者帳號, 「把合法 MCP 伺服器的木馬化分支發布到 PyPI 登錄,例如 tiktoken_mcp,並把惡意程式 碼直接注入組織的官方 GitHub 儲存庫,例如 azure-functions-mcp-extension」。對 MCP 伺服器下後門,意味著酬載是循著唯一一類編碼代理生來就會載入、並且會授予工具的相依 套件抵達的。

來源查核拿到一份有效的簽章。 任何把證明當成信任訊號的人,都該在這一段停一下:

DUSTMAKER 的樣本具備偵測自身是否執行於持續整合與持續交付(CI/CD)環境的功能。一旦 確認,它會從 GitHub Actions 執行器的行程記憶體中擷取 OIDC 權杖。DUSTMAKER 使用 這些權杖把自己授權為受信任的發布者,並發布帶有有效、經密碼學簽署之 SLSA Build 3 證明的遭入侵套件版本。以有效權杖發布的套件,會通過 AI 編碼代理的自動化信任檢查。

替那個套件簽名的,是發布者自己的身分。DUSTMAKER 從它所寄居的執行器行程記憶體裡把 身分撈了出來,所以一個檢查簽章的驗證器會回傳有效,而且它是對的。

端點代理盯錯了街區。 DUSTMAKER「把惡意檔案放進、或修改於供 AI 編碼助理與整合 開發環境(IDE)使用的隱藏專案工作區目錄(.claude/.vscode/.cursor/ 等)」。 GTIG 也說了原因:這些資料夾讓惡意程式融入「日常的開發者雜訊」,並避開端點偵測會盯著 的地方,例如登錄機碼與 /etc/cron.*。你的 EDR 廠商沒有為 .cursor/ 裡的新檔案出過 規則,因為直到今年,那個資料夾裡放的還是編輯器偏好設定。

助理拿到一個設定檔,而設定檔就是指令。 從那些同樣的隱藏目錄出發,DUSTMAKER 「使用惡意設定檔 … 指示 AI 助理在日常的開發者互動期間執行任意指令或腳本(例如 setup.mjs)。這實際上迫使 AI 模型在開發者不知情的情況下代替攻擊者執行指令」。 助理讀了自己工作區的設定,那是它的工作,而設定指名了一個要跑的腳本。這句話裡從頭到尾 沒有任何漏洞利用。

而稽核 API 刪掉了稽核軌跡。 這個惡意程式把自己的 CI 任務偽裝成 「Copilot Setup」之類的 AI 主題名稱,然後發出自動化 API 呼叫,把工作流程執行記錄從 GitHub 介面上移除。

五個讀者,五份攻擊者寫的產物讀者拿到了什麼得出什麼結論LLM 安全掃描器讀檔案一段它必須拒絕的註解生物武器,接著是武器設計什麼都沒有 — 它婉拒了記成無法取得分析來源驗證器讀簽章一份真的 SLSA Build 3 證明以竊來的 OIDC 權杖鑄出有效,而且判得沒錯那個身分真的是發布者的AI 編碼助理讀工作區設定一份攻擊者寫的設定.claude/ · .cursor/ · .vscode/執行 setup.mjs在一次日常互動之中端點偵測盯著惡意程式會寫的地方改寫進編輯器的資料夾不是登錄機碼,也不是 /etc/cron.*日常的開發者雜訊還沒有規則涵蓋那條路徑稽核者工作流程記錄已透過 GitHub API 刪除一段空的歷史
同一條攻擊鏈裡的五道防線,沒有任何一道故障。每一道都是對著產物形成判斷的讀者,而每一次,產物都是攻擊者寫的。掃描器拒絕了,驗證器驗證了,助理服從了,端點代理看向別處,稽核者找到一份空記錄。

五道全都照設計運作

你在這裡沒有錯誤可以回報。掃描器套用了它的安全政策,那正是你付錢請它做的事。驗證器 驗證了一份確實有效的簽章。助理載入了自己工作區的設定,那是功能。端點代理盯著的,是在 編碼代理還沒有設定目錄之前重要的那些路徑。每一個都對自己收到的輸入做了自己的工作, 而輸入是 UNC6780 挑的。

一般的規避要的是錯誤的判定:把二進位檔打包、把字串切開、把網址編碼,指望分析回來 是乾淨的。DUSTMAKER 要的東西,從一個正確的判定裡就能拿到,或者乾脆不需要任何判定。

任何形狀是讀取產物、形成意見的防禦,都在這個射程之內。把語言模型放在讀者的位子上 會讓射程更大,因為模型帶著自己那份不會做的事情清單,而那份清單是公開的、有文件的, 任何打得出一段註解的人都構得到。業界花了兩年把模型鍛鍊得不會被說服點頭。UNC6780 把它們說服到什麼都不說。

那麼,換一個問題去問你代理式流水線裡的那些控制:其中哪些必須讀取攻擊者所寫的內容 才能完成工作,而把那些放到一邊之後,還有什麼站得住。

沒得爭辯的檢查

Bromure Agentic Coding 讓每個編碼代理跑在你 Mac 上一個硬體虛擬化的 Linux VM 裡, 所有安全控制都在那道邊界的主機這一側。面對這樣一條攻擊鏈,真正關鍵的是這些控制拿 什麼當輸入。

年齡閘門讀的是一個時鐘。 主機端代理伺服器認得出對主要套件登錄的請求,並在任何 一個位元組抵達 VM 之前,套用該工作區的供應鏈政策。年齡閘門是預設唯一開啟的一層, 設定為至少兩天,會拒絕比截止點更年輕的版本。它靠改寫登錄的版本清單來做到這件事, 所以從代理的角度看,太新的版本根本還不存在;一次指定版本的直接抓取會回來一個 HTTP 451,內文說明該套件真正的年齡。套件本身從來不會被讀。一段為了讓語言模型出軌而 寫的註解,挪不動任何一個發布時間戳,而 UNC6780 的木馬化分支按其本質全都是新鮮發布。 npm、PyPI、Cargo、RubyGems 與 Packagist 全都帶有逐版本的發布時間,所以閘門涵蓋得到 它們。

分類器沒有政策可以搬出來。 Bromure 的原始碼偵測器會為代理串流回模型的 tool_result 區段評分,也就是檔案內容、網頁與指令輸出,用的是在你 Mac 上以 ONNX 執行、DeBERTa 家族的本地 PromptGuard 模型。它是序列分類器,不是生成模型。把 DUSTMAKER 的註解遞給它,它不組織任何答案,不掂量回答是否被允許這個問題,也不婉拒 任何東西。它只吐出一個介於零與一之間的數字,而一段以偽造系統覆寫開場、宣告安全準則 已暫停的文字,正是會把那個數字推高的形狀。每個工作區都由你決定命中時做什麼:記錄、 詢問,或封鎖。封鎖會回傳 451,模型永遠看不到那段內容。

讀工作區設定的,是代理以外的東西。 一個放在 .claude/ 裡、叫助理去跑 setup.mjs 的檔案,就是所謂的規則檔後門,而它有自己的偵測器:一輪針對隱藏 Unicode、 後設指令樣式、憑證路徑與 curl 導進 shell 這類構造的確定性啟發式掃描,再加上一個 為了那些不合任何固定樣式的流氓指令而微調的 ModernBERT 分類器。兩者都在代理伺服器裡、 在裝置上執行,處理的是從系統提示詞中抽出來的指令檔,那是 VM 內部任何東西都關不掉的 位置。兩者都是逐工作區的開關,而且第一次開啟時各要下載一次模型。那次下載,就是這個 故事向你要的五分鐘。

憑證竊取程式需要憑證。 DUSTMAKER 的存在就是為了從開發環境裡收割機密,而一個 Bromure 工作區一個也沒有。Bromure 把你設定的每一份憑證,替換成一個保留結構的假貨, 由真實值與每次安裝專屬的鹽推導而來:sk-ant-api03-brm-…、一個長度正確的 ghp_ 權杖、brm-mcp_…brm-k8s-…。這些假貨進到環境變數裡,也進到 ~/.git-credentials~/.docker/config.json~/.kube/config~/.aws/config, 而那正是竊取程式會逐一列舉的清單。你真正的值留在 Mac 上加密著,主機端代理伺服器再 把它們換到線路上,範圍限定在各自被鑄出來時對應的目的主機。SSH 私鑰的位元組永遠不會 進入 VM;跨過去的只有簽章。沒有任何開關管得到這些,因為代理伺服器就是 VM 通往網路的 唯一路線。

假貨同時也是絆索。 一個假權杖只有一個正當的目的地。代理伺服器會掃描每一個對外 請求的標頭與內文,找出正要去別處的假貨。一旦命中,它不轉發任何一個位元組就拒絕該 請求,當場暫停 VM,並在 Security Timeline 裡記下一筆紅色的 Credential brokering。它把該工作區標記為已遭入侵,而下一次啟動會先逼你清掉磁碟 與家目錄映像檔。事後你不必輪替任何東西,因為竊取程式握著的是佔位符,而那個佔位符 從沒出去過。

記錄留在你的機器上,構不到惡意程式呼叫的那個 API。 把追蹤設在 Activity only 時,代理伺服器會為每一個離開 VM 的請求寫下一列中繼資料,不含內文。 bromure-cli trace hostnames 會列出該工作區接觸過的每一個不同主機與次數,而 bromure-cli trace leaks 會在入侵警報之後指出目的地。一個握著 GitHub 權杖的攻擊者, 幾次 API 呼叫就能清空一段工作流程歷史。要清空那份追蹤,得先摸到你的 Mac。

會讀產物的控制它們每一個的輸入都是攻擊者寫的模型讀檔案給它一份它非拒絕不可的東西驗證器讀簽章偷走 OIDC 權杖;簽章是真的助理讀設定寫下設定;它就跑你指名的東西環境裡放著真的機密~/.aws/config · ~/.docker · GH_TOKEN攻擊者需要的一段註解,和一個帶著政策的讀者讀別的東西的控制在主機上、VM 之外、你的 Mac 上年齡閘門讀發布時間戳預設兩天;太新的版本不存在分類器吐出一個機率本地 ONNX · 記錄、詢問或封鎖 · 沒有可婉拒的餘地VM 裡只有佔位符sk-ant-api03-brm-… · brm-mcp_… · brm-k8s-…代理伺服器比對位元組與目的地越界的佔位符:451、VM 暫停、一筆紅色記錄攻擊者需要的改掉一個時間戳,或一份根本不在的機密
同一份產物,面對兩種檢查。一個會讀取攻擊者所寫內容的控制,可以被餵進為了讓它出軌而寫的內容。一個讀時間戳、讀位元組樣式、讀目的主機的控制,沒有什麼可以跟它爭辯,而竊取程式此行要找的憑證,從來就不在它執行的那台機器裡。

今天下午就能改的兩個設定

打開工作區的 Supply Chain 面板,確認年齡閘門是開著的,截止點設在你能接受的 值。兩天是預設值,而面對一個剛剛發布的木馬化分支,這是你手上最便宜的控制。接著 打開 Prompt Injection,把兩個偵測器都啟用;每個都會下載一次模型,之後掃描就 都在本地進行,而且免費。

一條讓問題變小的規則

Guardrails 裡把 Unmatched traffic 設成 Deny,然後列出這份工作需要 的東西:allow web api.github.comallow web registry.npmjs.orgdefault deny。一個連不上自己指令伺服器的竊取程式,只是一顆你正要丟掉的磁碟上的 一個檔案,而存檔會把規則推送到執行中的工作階段,不必重新啟動。

不存在勝過評估

我們一再從不同方向抵達同一個地方。一個 信任了代理寫得出來的名稱的出口代理伺服器。一個 把 bash 讀成和 bash 不一樣的指令守衛。一個 金鑰檢查失敗後落到空認證物件的 AI 閘道。 在每一個案例裡,某個元件都在誠實地做著評估某樣東西的工作,然後評估壞掉了。

DUSTMAKER 把這個論點磨得更利,因為它根本不需要任何壞掉的評估。它只要求:評估是在 UNC6780 寫的內容上進行。給讀者一個檔案,讀者就會對它形成看法;是誰提供了那個檔案, 決定了其餘的一切。

評估是比不存在更弱的原語,而我們卻不停地把不存在能免費完成的工作交給評估。你沒辦法 跟一個發布時間戳爭辯,而一個機率沒有想要收回的意願。一個以完整權限在可拋棄式 VM 裡 執行、把自己認得的每個設定檔都翻過一遍的憑證竊取程式,最後帶走的是一把佔位符,和 一個它繞不過去的通訊端。

把模型放進你的審查流水線是對的決定。只要確保那些真正會擋下東西的控制,是儲存庫裡 沒有任何東西講得上話的那些。 安裝 Bromure Agentic Coding,給你的代理一些永遠不必讀攻擊者檔案的 檢查。