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

那個儲存庫真的是 Microsoft 的

2026 年 6 月 5 至 6 日,Miasma 蠕蟲把竊取憑證的程式碼推進了 Microsoft 自家四個 GitHub 組織下的 73 個儲存庫——Azure、Azure-Samples、microsoft、MicrosoftDocs——其中包括官方部署 Action 的 Azure/functions-action,以及 durabletask 這個在 5 月已經被清理過一次的儲存庫。這一次,酬載沒有等到 npm install。它在開發者於 Claude Code、Cursor、Gemini CLI 或 VS Code 裡開啟該儲存庫的那一刻就觸發了。這裡說明為什麼信任訊號——「這是 Microsoft 的儲存庫」——再一次成了攻擊面,以及當開啟它的代理跑在按設定檔隔離的 Bromure VM 裡、藏在一個憑證仲介、一道讀寫護欄、以及一段套件冷卻期之後時,什麼會改變。

一週前,這個蠕蟲搭著 Red Hat 的 npm scope 混進來,透過一個 preinstall 掛勾觸發。這一週,它搭著 Microsoft 的 GitHub 混 進來,而且根本不需要任何安裝。Miasma 把設定檔範圍的組態植進了 73 個 Microsoft 所有的儲存庫——其中包括官方部署 Action 的 Azure/functions-action——而酬載在開發者於 Claude Code、Cursor、 Gemini CLI 或 VS Code 裡開啟該儲存庫的那一刻就執行了。觸發點不是 clone。是在你的代理裡開啟它。

一個為了除錯一次失敗的部署而 clone Azure/functions-action 的 開發者不會猶豫,他指向它的代理也不會。這是來自 Microsoft 自家 Azure 組織的第一方儲存庫——也就是半個生態系都以 Azure/functions-action@v1 引用的那個 GitHub Action 的權威來源。 沒有維護者需要審查,因為維護者就是 Microsoft。沒有名字需要瞇起 眼睛看,因為名字分毫不差就是它該有的樣子。於是儲存庫被開啟了, 而代理就照每一個現代編碼工具在開啟資料夾時的做法,去讀取專案的 組態——而其中一個組態檔指向一個命令,那個命令是一個約莫 4.3 MB 的 blob,它一開始就在掃描檔案系統找金鑰。

我們在七天前 寫過這場同一行動裡的 npm 那一半。 Miasma 是同一個蠕蟲——是 TeamPCP 在 5 月中公開釋出的那段「Mini Shai-Hulud」程式碼的變種——而它 所示範的那個令人不安的事實也是同一個,只是又轉緊了一圈:信譽 良好的來源不是一道資安控制。它感覺起來像。大多數供應鏈建議都 仰賴它。而在 6 月 5 日,它換來的什麼也沒有,而且是雙重落空: 命名空間是 Microsoft 的,而執行也不是來自一個你選擇安裝的套件。 它來自開啟一個資料夾。

Miasma 對 Microsoft 的儲存庫做了什麼。

2026 年 6 月 5 至 6 日,GitHub 在惡意 commit 被推進之後停用了 橫跨 Microsoft 四個 GitHub 組織的 73 個儲存庫,這是根據 The Hacker News 以及來自 StepSecurity 的一份詳盡拆解。 Redmond Magazine 在 6 月 8 日做了報導。分項如下:

  • Azure — 49 個儲存庫,包括 Azure/functions-action(官方的 Functions 部署 Action)以及 .NET、Python、Java、Go、PowerShell 的語言 worker。
  • microsoft — 10 個儲存庫。
  • Azure-Samples — 13 個儲存庫。
  • MicrosoftDocs — 1 個儲存庫。

剝到只剩機制的樣貌:

  • 入口是一個先前被攻陷、具有 commit 權限的貢獻者帳號,被用來 把惡意 commit 直接推進這些儲存庫——並標記為 [skip ci],使這些 變更溜過了原本會執行的 CI/CD 檢查。
  • 這個 commit 植入了設定檔範圍的組態——也就是當你開啟資料夾時, 編碼代理或 IDE 會自動去讀並據以動作的那類檔案:一個編輯器任務、 一個代理掛勾、一個專案定義的 MCP 伺服器。這正是 Adversa AI 的 TrustFall 在 Claude Code、Cursor CLI、Gemini CLI 與 Copilot CLI 上示範過的 同一類信任邊界——這四者都會在資料夾信任提示之後立刻執行專案 定義的組態。
  • 酬載——約莫 4.3 MB 的混淆程式碼——在該儲存庫於 Claude Code、 Gemini CLI、Cursor 或 VS Code 裡被開啟時執行,或透過一個 npm test 指令稿執行。而不是僅靠 clone。 真正讓它跑起來的, 是把你的代理指向那棵被 clone 下來的樹這個動作。
  • 一旦執行,它就會在主機上搜刮 GitHub 權杖、AWS 金鑰、Azure service principal、GCP 憑證、npm 與 PyPI 發佈權杖、SSH 金鑰, 以及 .env 檔案,然後利用竊得的存取權限把自己提交到下一處—— 而這正是讓它成為一個蠕蟲、而非一次性攻擊的原因。

有一個細節值得多停留一下:Azure/durabletask 也在中招的儲存庫 之列——而它在 5 月的 TeamPCP 行動裡就已經被攻陷過一次並清理 完畢。一個曾經被修復過的儲存庫,在五週後又被重新下毒。清理不是 一個你抵達後就能保持住的狀態;它是一個只要鏈條上另一份憑證被 拿走,你就會在那一刻退回去的狀態。

同樣值得精確說明的是哪些事情並未發生。Microsoft 的企業網路 沒有被攻破。Azure 這個雲端服務沒有被攻破。沒有任何客戶資料、沒有 任何正式環境系統被碰到。這是一次對原始碼儲存庫的攻擊——而它 波及最廣的後果,跟惡意程式本身毫無關係:在 GitHub 停用 Azure/functions-action 的那一瞬間,地球上每一條引用 Azure/functions-action@v1 的流水線都解析不出來了。Microsoft 是 最高調的帶原者。真正被攻陷的,是那些在 6 月 3 日到 5 日之間於 代理裡開啟了有毒儲存庫、憑證被從自己機器上搜刮走的開發者。

開發者筆電 — 主機 secrets 對代理在開啟資料夾時所跑的任何東西可見MICROSOFT 自己的 GITHUB被攻陷的貢獻者帳號 → push [skip ci]Azure/functions-actionAzure/durabletask(再次)+ 植入專案組態開發者 CLONEgit clone …/functions-action尚未執行任何東西第一方儲存庫 ⇒沒有理由猶豫在編碼代理裡開啟 — 酬載觸發Claude Code · Cursor · Gemini CLI · VS Code開啟資料夾 → 讀取專案組態代理掛勾 / 任務 / MCP 伺服器↳ 執行 4.3 MB 酬載(僅 clone 並不會跑它)主機檔案系統 & ENV — 全部真實,酬載全可讀$GITHUB_TOKEN, gh hosts.ymlpush 存取(真實)~/.aws, Azure service principal雲端金鑰(真實)~/.npmrc, PyPI 權杖在此發佈(真實)~/.ssh/id_ed25519, .env金鑰 + CI secrets(真實)tar | encrypt | exfiltrate→ 從你的機器上被搜刮走自我傳播竊得的 GitHub 存取提交到下一個儲存庫植入同一份組態73 個儲存庫, 4 個組織durabletask:中招兩次
Miasma 在一台於編碼代理裡開啟受影響 Microsoft 儲存庫的開發者機器上的樣子。一個先前被攻陷的貢獻者帳號推送一個 [skip ci] commit,植入設定檔範圍的組態——一個代理掛勾、一個編輯器任務、一個 MCP 伺服器定義。開發者 clone 該儲存庫(沒有任何東西執行),並在 Claude Code、Cursor、Gemini CLI 或 VS Code 裡開啟它。一開啟資料夾,代理就讀取並據那份組態動作,執行一個 4.3 MB 的酬載,在主機上搜刮 GitHub 權杖、AWS 金鑰、Azure service principal、npm/PyPI 權杖、SSH 金鑰,以及 .env 檔案,加密後送出去,並利用竊得的 GitHub 存取權限把自己提交到下一個儲存庫。沒有 typosquat,沒有假維護者,沒有 npm install。信任訊號是 Microsoft 組織名,而執行來自開啟一個資料夾。

同一個儲存庫,在 Bromure Agentic Coding 裡開啟。

Bromure Agentic Coding 讓你的編碼代理跑在一個 按設定檔隔離的 Linux VM 裡——它有自己的核心、自己的檔案系統、 自己的網路堆疊,架在 Apple 的 Virtualization 框架上。一個設定檔 就是一個連貫的工作範圍:這個客戶這個內部服務這個你為了 除錯一次部署而 clone 下來的開源儲存庫。你把 Azure/functions-action clone 進那個設定檔,並在裡頭用你的代理 開啟它。開啟資料夾這個觸發點完全照設計觸發。酬載跑起來。然後它 去找金鑰,找的卻是一個它搆不到的主機。

因為憑證不在設定檔裡。這個 VM 出廠時帶著 stub——一些對 gitghawskubectlnpm,以及任何期待一個 Authorization 標頭的東西看起來都很真的假權杖。你 Mac 上的一個 proxy 坐在每一條 離開沙箱的連線前面,認出那個 stub,並在請求離開時於線上把它換成 真正的 secret(那個握著金鑰的沙箱 逐步講解了這個機制)。真正的 GitHub PAT、真正的 AWS 金鑰、真正的 Azure principal——它們沒有一個碰到 VM 能讀的檔案、環境變數或一頁 記憶體。SSH 金鑰根本就不離開 macOS Keychain;只有 ssh-agent 通訊端被轉發進去,就如 OpenSSH 一向的設計意圖。

那麼把 Miasma 的搜刮走過這道邊界。酬載在環境裡找 $GITHUB_TOKEN, 找到一個 stub。它讀 ~/.aws,什麼都沒找到。它讀 ~/.npmrc,找不到 發佈權杖。它讀 ~/.ssh,找不到金鑰檔案——那裡有的是一個被轉發的 通訊端,而不是一把躺在磁碟上的私鑰。那個 4.3 MB 的 blob 完全照 所寫的跑到結束。它只是外洩了一個從來就沒握著你金鑰的盒子,而這個 盒子,跟所有要緊的東西之間,隔著一道由硬體強制執行的邊界,還站 錯了邊。

按設定檔隔離的 BROMURE VM代理開啟 functions-action專案組態 → 酬載觸發會跑,但只在訪客內部酬載看到的東西$GITHUB_TOKENstub_7f3a…~/.aws, ~/.npmrcstub / 不存在~/.ssh 金鑰檔只有通訊端傳播:git push(自己)需要寫入 → 問 proxy外洩主機金鑰:無可拿走需要相依套件?npm install → 抓取離開 VM經由 Bromure 的 proxyPROXY · 你的 MAC憑證仲介真正的 PAT / 金鑰在此stub → 真值, 在線上取值絕不進入 VM護欄:讀 / 寫push = 變更 → 提示點明動詞 + 目標供應鏈OSV + socket.dev 掃描冷卻期:< 2 天扣住安裝指令稿被剝除結果主機金鑰:搜刮到的是 stub,真值未被碰到傳播用的 push:暫停, 你說不全新的壞套件:根本到不了 VM爆炸半徑 = 1 個設定檔
三道層次,按 Miasma 撞上它們的順序。(1)供應鏈:proxy 把每一次套件抓取都拿去對 OSV 和 socket.dev 掃描,並把比冷卻期還年輕的版本隔離起來——所以當生態系還在追趕時,代理連一個全新的惡意相依套件都拉不下來。(2)憑證仲介:VM 只握著 stub;真正的 secret 坐在主機上、藏在 proxy 之後,在線上才被換進去,所以酬載的主機搜刮找到的是佔位符。(3)護欄:蠕蟲的傳播步驟——一次把自己提交出去的 git push——是一次會改變狀態的呼叫,而一道讀寫護欄在線上把它擋下並發問,點明動詞與目標。每一道層次都在代理之下、在代理無法繞過的 VM 邊界上被強制執行。

傳播步驟是一次寫入,而寫入會收到提示。

竊取金鑰只是 Miasma 的一半。另一半是散播:它利用竊得的 GitHub 存取權限把自己提交到下一個儲存庫,而這正是把一小撮有毒儲存庫變成 73 個的東西。即使在一個合法擁有 push 權限的設定檔裡——比方說你 clone functions-action 恰恰就是因為你打算對它開一個 PR——蠕蟲的 傳播步驟還是得透過 proxy 出去,而那正是 Guardrails 攔住它的 地方。

Guardrails 讀的是操作,而不只是連線——它分得出讀和寫。一次 git fetch 是讀;一次 git push 是寫。把一個設定檔的 GitHub 憑證設成寫入時詢問,那麼在代理伸手去做一個會改變狀態的呼叫的 那一刻——蠕蟲把它的組態提交回去所需要的 git-receive-pack、對某個 API 的一次 DELETE、對 EC2 的一次 Terminate*——Bromure 就在線上 把它擋下,並在你的 Mac 上浮出一個點明動詞、目標與設定檔的提示。 你給出的授權是有時限的:發佈一次給十五分鐘,嚇人的那些只給單次 使用,請求說不通時就絕不給。讀取從不打斷你;代理整天 fetch、grep、 讀檔都行。會暫停的是變更

這就是「代理有一個權杖」和「代理可以用那個權杖為所欲為」之間的 差別。Miasma 整套散播機制就是一次代理從沒告訴過你它在做的寫入 ——而一次代理從沒告訴過你它在做的寫入,正是這個讀寫提示生來要逮的 東西。那個傳播蠕蟲的 push,變成一個你按下不允許的對話框,正如 「代理刪掉了正式環境資料庫」 不再是一份事後檢討、 而變成一個你拒絕掉的提示。

那個版本只有幾小時大,而 Bromure 讓套件變老。

Miasma——以及更廣的 Mini Shai-Hulud 血脈——還有第二條觸及開發者的 路徑:不是透過一個你開啟的儲存庫,而是透過一個剛被下毒、由代理在 做事時安裝下來的套件。這場行動的 Red Hat 那一半正是如此,一個 掛在某個受信任 scope 下 32 個套件上的 preinstall 掛勾。而那些 事件殘酷的細節在於時機:一個被攻陷的版本通常在數小時內就會被 逮到並撤下——但那幾小時,正是一個無人看管、自主執行的代理可能把 它拉下來的那幾小時。

Bromure 的 Supply Chain 層把同一個邊界 proxy 變成一個掃描檢查 哨,而它做了兩件在面對當天即遭攻陷時真正要緊的事:

  • 它強制把每一次抓取都拿去對 socket.dev 以及 OSV 掃描。 OSV 逮的是超過你所設嚴重度門檻的已知 CVE。socket.dev 逮的是漏洞 資料庫還沒追上的東西——惡意安裝指令稿、行為型惡意程式、 typosquat、剛剛發佈的那次攻陷。一個被標記的版本在 tarball 落進 VM 之前就被攔下。關鍵在於,這次掃描跑在代理之下、在 proxy 上: 無論代理如何改寫自己的組態來繞過你,那次抓取還是得從它無法跨越的 邊界離開。
  • 它強制執行一段冷卻期。 Bromure 把任何在過去兩天內發佈的版本 隔離起來——可調整——所以一個一小時前上傳的版本,在那個設定檔裡 就是無法安裝,直到生態系追上來。面對一個整個機會之窗就是 發佈撤下之間那道縫隙的蠕蟲,冷卻期不是一個用來判斷套件看 起來壞不壞的啟發法。它是一種拒絕當第一個去發現的人的姿態。把它跟 Bromure 即時做的安裝指令稿剝除結合起來——把 postinstall 掛勾從 tarball 裡拉出來,並修好 metadata 雜湊好讓安裝照樣通過驗證——那麼 真的落地的那個套件,落地時就是惰性的。

對 Miasma 來說,開啟儲存庫這條路徑是頭條。但同一場行動也透過套件 散播,而冷卻期正是那道會餓死它 npm 那一面的控制:一個全新的 @redhat-cloud-services 版本,或一個在你除錯那個 Microsoft 儲存庫 時被拉進來、剛被下毒的傳遞相依套件,會在它正危險的那幾個小時裡 被扣在隔離區裡。

這在哪裡救不了你。

一次你批准的 push,就是一次會發生的 push。

讀寫護欄逮的是代理沒告訴你的那次寫入。它不讀 diff。如果你正在 合法地 push 到 functions-action,而你批准了那個提示,Bromure 就會轉發那次 push——原則上也包括一個你沒在 diff 裡注意到的有毒 工作流程。讀你批准的東西。 工作階段追蹤會告訴你 該讀哪些 diff。

冷卻期是一道窗口,不是一道牆。

兩天是依觀察到的發佈到撤下的縫隙調出來的,但一個有耐心的攻擊者 可以坐在一個被攻陷的版本上比冷卻期更久,到了第三天照樣可安裝。 冷卻期餓死的是當天即發作的蠕蟲;它並不會替一個只是單純變老的 套件背書。socket.dev 和 OSV 還是得各盡其職。

設定檔是長期存活的,所以持久化會持久。

一個 Bromure 設定檔不是一片可拋棄的磁碟。一個把自己寫進設定檔內 某個啟動路徑的酬載,能存活到那個設定檔的下一次工作階段。它醒來 時迎接它的,是一個沒有主機金鑰的訪客,以及一個只說短壽命、需經 提示、範圍受限權杖的仲介——存在於一個空無一物的盒子裡——但終究 是存在。

刻意去界定仲介的範圍。

如果一個設定檔今天被佈建成可以 push 到某個儲存庫,而那個設定檔 今天跑了 Miasma,一次被批准的寫入就會通過。仲介的授權之所以有效, 是因為它們很窄。一個只需要讀某個儲存庫的設定檔,就不該能夠寫它; 一個從不發佈的設定檔,就不該握有發佈權杖。隔離圍堵爆炸;界定範圍 決定它最大究竟能有多大。

下一個受信任的儲存庫,已經被 clone 在某處了。

TanStack 蠕蟲的教訓是,鎖定檔案 和簽名都不是防線。Red Hat scope 的教訓是,發佈者也不是。Microsoft 補上了下一個推論:儲存庫也不是, 而且那個觸發點甚至不必是一次你選擇的安裝——它可以是一個你的代理 開啟的資料夾。Azure/functions-action 儲存庫並沒有因為受信任而做錯 什麼。受信任正是一個權威第一方 Action 存在的全部意義,而這恰恰也是 讓它值得下毒的東西——在 durabletask 這個例子裡,還下了兩次。

你沒辦法靠更謹慎地信任來修好這個,因為這份信任從來就沒放錯地方。 你修好它的方式,是把事情安排成讓「這是哪個儲存庫」和「這是哪個 scope 發佈的」不再是你的 keychain 所仰賴的那些問題。 Bromure Agentic Coding 就是那種組態:代理在一個 按設定檔隔離的 VM 裡開啟儲存庫,真正的憑證藏在仲介之後留在主機上, 代理做的每一次寫入都得先過一個提示,而一個套件在它熬過冷卻期之前 裝不上去。一次有毒的開啟資料夾,最壞能做的就是外洩一個從來沒握著 你金鑰的盒子。它免費、開源,今天就發行。