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

旗標說了不

2026 年 8 月 3 日,The Hacker News 報導了 FaceHugger:Hugging Face 的 Diffusers 函式庫——每月七百萬次下載——裡的三個缺陷,讓一個精心構造的模型儲存庫得以在載入它的機器上執行任意程式碼,即使已經設定 trust_remote_code=False。根本原因:信任檢查與程式碼載入發生在兩個不同的時間點。程式碼代理抓模型就像抓套件一樣,所以那面說謊的旗標守的是開發者的筆電。Bromure Agentic Coding 把邊界放在行程之下而不是行程之內:載入在一台可拋棄的 VM 裡執行,伸手可及的憑證是誘餌,酬載打回家的電話死在主機代理伺服器上——惡意儲存庫讓攻擊者賠上酬載,而你什麼都不賠。

儲存庫裡有一個名叫 None.py 的檔案,整個把戲就是這樣。安全旗標設了, 檢查跑了,程式碼照樣執行了,因為檢查和載入發生在兩個不同的時刻—— 而攻擊者只需要在第二個時刻在場。

2026 年 8 月 3 日,The Hacker News 轉載了 Zafran Labs 在七月底發表的研究: Hugging Face 的 Diffusers 函式庫裡的三個弱點,合稱 FaceHugger,讓一個 精心構造的模型儲存庫得以在任何載入它的機器上執行任意 Python 程式碼。 Diffusers 是執行擴散模型的標準途徑——每月約七百萬次下載,安坐在正式環境 的管線、CI 工作、筆記本和容器映像裡。這個函式庫正好為這種情境準備了 一道防護:trust_remote_code,一面本該阻止模型儲存庫裡未經審閱的程式碼 在你機器上執行的旗標。三個缺陷全都在它設為 False 的情況下從旁走過。

檢查和載入是兩個不同的事件

從 Hub 載入模型不是單一一個操作。函式庫會依序發出兩個 HTTP 請求: 第一個抓取儲存庫的設定,第二個抓取該設定所指向的程式碼與權重。 trust_remote_code 這道閘門在第一個請求期間做它的檢查。它要防範的 東西,在第二個請求裡才抵達。

Zafran 的研究人員找到了穿過這道縫隙的三條路,如今已配上 CVE 並在 Diffusers 0.38.0 修補。在 CVE-2026-44827(CVSS 8.8)裡,一個字串 格式化的怪癖會在沒有要求自訂管線時組出檔名 None.py——而對這個檔案的 存在性檢查跑在一條不會標記它的程式碼路徑上,於是一個帶著名為 None.py 檔案的儲存庫就通過閘門、在載入時執行。CVE-2026-45804 是一場競速:兩個 請求之間隔著大約三分之一秒,一個在這期間發生變化的儲存庫,被驗證的是 一個東西,被執行的是另一個東西。CVE-2026-44513 則在本地快照載入時整個 跳過閘門。Zafran 的研究人員把三個壓縮成一句話:任何能讓載入器看到 閘門沒看到的自訂程式碼的方法,都能繞過。

這個模式是有前科的。Transformers——Hugging Face 生態系的另一根支柱—— 有過它自己的 trust_remote_code=False 繞過(CVE-2026-4372),透過一個 標準的模型載入呼叫即可利用。這類漏洞是隨設計而來的:一個安全承諾住在 它所監管的行程裡面。旗標是一個函式引數,而強制執行卻散落在程式碼路徑、 檔名格式化和請求時序之間。當其中任何一個和旗標意見不合,輸的就是旗標, 而且沒有任何東西告訴你它輸了。模型照樣載入,展示照樣能跑。

你的代理在某個星期二載入模型

從程式碼代理的椅子上重讀這個故事。「從 Hub 抓那個 inpainting 管線, 跟我們的跑個基準測試」是再普通不過的星期二任務。專案釘住 diffusers==0.37,因為專案就是會釘住東西。代理寫下一位謹慎工程師 會寫的東西——from_pretrained(..., trust_remote_code=False)——而謹慎 的版本正是有弱點的版本。套件的原始碼你可以在安裝前讀一遍;模型儲存庫 則是設定、權重,以及託載入器之福、第二個 HTTP 請求說它是什麼它就是 什麼的東西。

代理讓這條載入路徑變得火熱。它們抓模型就像抓套件,任務進行到一半、 憑名字就抓,而攻擊者早已想通推薦本身就可以被下毒。 一個在載入時引爆的模型儲存庫,是同一招換到隔壁的登錄庫,而且偽裝 更好:酬載安坐在一個你歸檔為資料、而非程式碼的產物裡。

Zafran 的建議是把模型儲存庫當成不可信任的程式碼對待。正確——但它有 一個報告沒有著墨的前提:一個讓不可信任的程式碼可以執行、而你毫無 損失的地方。懷疑的政策是一種紀律,你維持它,直到截止日期開始大聲。 一間為引爆而造的房間,不需要你的紀律。

載入發生在盒子裡

Bromure Agentic Coding 就是那間房間,而它從 FaceHugger 的旗標失守的 地方開始:邊界坐在行程底下,在一個載入器的內部無從爭辯的環境裡, 無論行程配不配合都會強制執行

把這場攻擊沿著一個設定檔走一遍。你的代理接下基準測試任務,連釘住的 版本一起,對惡意儲存庫呼叫載入器。None.py 執行了——在一台可拋棄的 Linux VM 裡,隔著一層虛擬機器管理程式才到 macOS。酬載此刻拿下的機器, 是一台為這個任務而存在、只裝著這個任務的工作區、工作合併後就被拋棄 的機器。

酬載的第一步真正行動是對外的。它非如此不可:抓第二階段,或者把找到 的東西送回它的操作者。這些請求每一個都要穿過主機代理伺服器,目的地 在那裡比對你為這個設定檔核准的清單。huggingface.co 在清單上——模型 就是這樣進來的。攻擊者的投遞箱不在。線路拒絕了,就像閘門本來 應該做的那樣——只是這次的 拒絕不取決於函式庫的程式碼路徑彼此意見一致。代理伺服器不知道、也 不在乎是哪個 Python 檔案在發請求。它比對目的地,找不到任何核准項目, 就把連線掐斷。

假設酬載跳過網路,改去翻箱倒櫃,翻環境變數和金鑰檔案。它找到的是 中介的誘餌。真正的值住在主機上, 只為核准的目的地在線路上注入——密碼從來不在 VM 裡,無從偷起。而如果它想 讓自己住下來——改寫某個設定、把什麼東西關掉—— 破壞性的形狀會停下來,等主機上的護欄對話框, 何況在一台壽命只有一個任務的機器裡搞持久化,本來就是虧本生意。

與此同時,任務成功了。基準測試跑完,數字回來了,你讀著 diff。攻擊者 花掉一個供應鏈上的位置,換來一個在沒東西可偷、沒辦法打電話回家的 房間裡執行的酬載。你什麼都沒花——連注意力都沒花,除非你把讀代理 伺服器日誌當運動。

行程內部——旗標住的地方請求 1 · 抓取設定trust_remote_code 在這裡檢查判定:「沒有自訂程式碼」✓~0.3 秒——沒有複查請求 2 · 抓取程式碼None.py 抵達——並執行閘門沒看見它 (CVE-2026-44827)承諾與強制執行,是同一個行程裡兩個不同的時刻。行程之下——Bromure 設定檔檢查的地方可拋棄的 Linux VM代理 · 載入器 · None.py——全部,有無旗標皆然伸手可及的憑證:誘餌任務結束即拋棄每個請求主機代理伺服器huggingface.co → 允許攻擊者的投遞箱 → 拒絕真金鑰在線路上注入
檢查在哪裡跑,又本該在哪裡跑。Diffusers 的信任閘門在第一個 HTTP 請求裡檢視設定;程式碼在第二個請求裡抵達——並執行。Bromure 設定檔不裁判載入器的內部:整個行程跑在一台可拋棄的 VM 裡,而每一個對外請求,不管是誰發的,都在主機代理伺服器上比對你核准的目的地清單。

修補函式庫,留下房間

Zafran 報告裡的修補建議要組織升級,然後出門打獵:那些釘住舊版 Diffusers 的應用程式、筆記本、推論服務和容器映像。這場狩獵沒有終點 線——在你的依賴樹某處,總有某個專案釘住那個帶縫隙的版本,而下一個 FaceHugger 會是另一個函式庫的旗標配上另一種繞過。守衛和 shell 意見不合是這種失敗在指令 允許清單裡的版本;沙箱只是一句話 是這種失敗在提示詞裡的版本。每一次,都是一個檢查住在它所檢查的東西 裡面,而那個東西贏了。

設定檔的邊界不會共享這種命運,因為它不參與它所圍住的行程。VM 不解析 管線設定。代理伺服器沒有 None.py 的程式碼路徑。它們唯一的工作—— 這台機器可拋棄、這條線路拒絕清單外的目的地——在繞過被公開的那天,和 被修補的那天,是同一份工作。一位 Sumo Logic 的 CISO 在評論這項研究時 心裡並沒有 Bromure,卻點名了那些守得住的防禦:「不光鮮的那些:出口 控制、分段,以及憑證衛生。」 我們把不光鮮的那些,做進了一個你按一下就有的設定檔。

繼續抓模型吧——槓桿就在那裡,你的代理本來就該替你試用它們。只是別再 讓一個函式引數,當 Hub 和你筆電之間唯一的屏障。 安裝 Bromure Agentic Coding,給代理一個規則守在 線路上的設定檔,下一次某個載入器的承諾破掉時——總會有一個破掉—— 新聞故事仍然是別人家的。