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

provider 在金鑰所在之處執行

Aikido 發現一個北韓的 RAT,以兩個 Terraform provider 的形式散布在 HashiCorp 的登錄中心上。terraform init 會下載 provider,terraform plan 會把它當成本機行程執行,而 provider 會讀取你的雲端憑證,因為 provider 本來就是用來做這件事的。這個酬載只對某一位受害者的變數名稱解密,所以把套件拉下來的掃描器看到的都是毫無作用的程式碼。被觸及的十八台機器中有十台是 Mac。Bromure Agentic Coding 給那個執行檔一台 VM、一把假的密鑰,以及一份它可以呼叫的目的地清單。

業界為編碼代理打造的每一項控制,都位在指令的上游:核准計畫、閱讀 diff、把關 push。沒有任何一項站在一個下載下來的 provider 執行檔與 ~/.aws 之間。

你正要往一個 stack 裡加一個容器。你向代理要一個 Docker provider,它寫了四行 HCL,你執行 terraform init。Terraform 從登錄中心解析出這個 provider、下載它、 檢查它的簽章,然後放進快取。接著你執行 terraform plan,Terraform 就把那個執行檔 當成你機器上的一個行程啟動,並透過 RPC 與它對話。過程中它會向你的雲端帳號認證, 用的是你自己在用的那套環境變數與那份 ~/.aws 設定檔。

這每一步都是文件裡寫明的設計,而這個月它同時也是一條惡意程式的投遞路徑。

Aikido 發現了什麼

9 月 22 日,Aikido 通報 了一個以 Go 寫成的遠端存取木馬,透過兩個 Terraform provider 與兩個 Go 模組散布。 The Hacker News 隔天跟進報導, 稱這是攻擊者首次利用 HashiCorp 營運的登錄中心投遞酬載。Checkmarx、JFrog 與 SafeDep 也補上了各自的分析。

這兩個 provider 是 gocommunity-io/dockerd,下載數 222,以及 kreuzwenker/docker,下載數 1,449。後者與真正的 provider kreuzwerker/docker 只差一個字母,而後者的下載數據宣稱有 5,600 萬。兩個 Go 模組 分別是 8 月 11 日發布的 gocommunity.io/orderedbtree 與 9 月 8 日發布的 gogets.dev/btreex。

Aikido 把這次行動連結到 Graphalgo,ReversingLabs 在二月記錄過它,並歸因於北韓的 操作者。同一把簽章金鑰自四月起就出現在 npm 的酬載裡。人們是透過一份工作邀約走到 這些套件面前的:一間假造的 Web3 公司在 LinkedIn 或 Facebook 上接觸開發者,送來一 份程式作業。作業的儲存庫是乾淨的,它拉進來的相依套件才是惡意程式。

provider 是一支程式,而 Terraform 把金鑰交給它

npm install 會執行套件的安裝腳本,然後退場。terraform init 取回的是一整支 程式,而執行那支程式才是重頭戲。

Terraform 的 provider 是一個由 Terraform Core 以 RPC 呼叫的可執行檔。 Terraform 會執行它,因為執行它正是 provider 的定義。你不會找到可以拔掉的安裝 掛鉤,也不會找到可以稽核的生命週期腳本,因為在這裡執行是功能本身,而不是解開 tarball 的副作用。

這個執行檔還跑在你網路上通往正式環境路徑最短的那個位置。Purple Shield 這樣描述那台機器: 「因此,一個惡意的 provider 會在部署你基礎設施的那個人的機器上執行,帶著那個人所 擁有的一切權限。」以及:「一台會執行 terraform apply 的筆電,手邊通常就有雲端 供應商金鑰、Git 權杖、SSH 金鑰和 Terraform 狀態檔。」

npm 上的竊取程式得在你的家目錄裡四處翻找憑證。Terraform 直接把憑證遞給 provider。

文件裡的路徑,以及被加上去的那個分支登錄中心kreuzwenker/dockerinit負責部署的那台機器Terraform Coreplan → 啟動,再 RPCprovider 行程你下載來的執行檔憑證~/.aws、權杖、金鑰沒有人要求的那個分支,就在 provider 裡sha256(containerName + networkID)相符 → AES 金鑰 → 解密 → go run .其他任何組態,以及所有掃描器雜湊不相符;酬載永遠不會被解密它唯一指名的那個組態一個 RAT,回報主機名與硬體,然後等待18 台機器、725 則簽到訊息:Windows 3、Linux 5、macOS 10
這條鏈裡沒有任何漏洞利用。terraform init 從登錄中心解析出 provider 並快取執行檔;terraform plan 把它當成本機行程啟動,並以 RPC 與它對話;provider 讀取環境中的憑證鏈,而這就是協定本身。Graphalgo 的 provider 只多加了一個分支:一個隱藏的進入點,會對你自己的兩個 Terraform 變數做雜湊,只有在雜湊值與一個寫死的值相符時,才會用那個雜湊當作 AES 金鑰,解開藏在範例夾具裡的酬載,並以分離的 'go run' 執行。對其他任何組態,這段程式碼都毫無作用。

只寄給一個人

這些 provider 只攻擊一個目標,對所有其他安裝它的人視而不見。

兩者都在 /internal/provider/resource_docker_container_funcs.go 裡帶著一個隱藏的 進入點,只有當 Terraform 變數 containerName 與 networkID 串接後的 SHA256 等於 某個寫死的值時才會觸發。接著程式碼用那個雜湊當作 AES 金鑰,解開套件內的一個檔案 路徑,把找到的東西解壓縮,逐一解密每個檔案,再以分離的 go run . 當成 Go 套件 執行。他們把壓縮檔藏在 examples 目錄裡,檔名叫 import-resource.sqlite3。

也就是說,攻擊者必須知道你給那兩個變數取了什麼名字,而這個攻擊者知道:工作邀約 這個誘餌,就是他學到名字的方法。每一位把套件拉下來的研究者、每一個掃描器,拿到 的都是坐在那裡什麼也不做的程式碼。你無法偵測永遠不會解密的東西。

第二階段是一個遠端存取木馬。它先回報系統資訊,包括硬體屬性、作業系統、主機名, 以及這台機器上有沒有 node,然後在兩個地方等待指令。一個是 Slack:一個叫 portfolio-devs 的工作區、一個叫 frontend-devs 的頻道,之後還有第二個工作區, 裡面有個叫 qa-announcements 的頻道。Aikido 在那裡數到 1,240 則加密訊息,最早可 回溯到 7 月 16 日。另一個是 Arbitrum Sepolia 測試網上的一份智慧合約,位於一個寫死 的位址,含有 1,402 筆交易,最早回溯到 8 月 6 日。惡意程式每三秒輪詢一次區塊鏈, 每十秒輪詢一次 Slack。

這兩條通道都是通往信譽良好目的地的普通 HTTPS。你的開發機器整天都在跟 Slack 說 話,而其中一台跟公開的 Ethereum RPC 端點說話,在任何一週團隊有東西要上鏈時看起來 都很正常。同一批人在 Checkmarx 上週描述的那個 npm 套件裡也用了 Sepolia 合約,他們 大方地重複使用,因為大方地用也行得通。

Aikido 從攻擊者自己的 Slack 裡撈出了受害者清單:725 則簽到訊息,涵蓋 18 個不重複 的主機名。三台 Windows、五台 Linux、十台 macOS。

在 Bromure 工作區裡有什麼不同

這場行動要的是憑證。在 Bromure Agentic Coding 裡,VM 的 ~/.aws/config 並不含 你的密鑰。它指向一個 credential-process 輔助程式,透過 virtio socket 交出你真正 的存取金鑰 ID,搭配一把 40 個字元的假密鑰。手冊列出了完全不必設定就會採用 它的工具:每一個 AWS SDK、aws CLI、boto3,以及 terraform。provider 照它預期的 方式讀取憑證鏈,拿到一份格式完好的設定檔,然後用一把一文不值的密鑰替請求簽章。

主機會在出口把那個簽章修好。它的 resigner 認得送往 *.amazonaws.com 的流量,剝掉 客體那註定失敗的 SigV4 簽章,在請求離開你的 Mac 之前用真正的密鑰重新簽。你的 terraform plan 照常運作。AWS 則對被偷走的那份憑證副本回以 InvalidSignatureException。

接著那把假密鑰想要出去。代理伺服器會掃描每一個外送請求的標頭與內文,尋找工作區 自己的佔位值,用的是便宜到可以掛在全部請求上的 Aho-Corasick 自動機。把一把假密鑰 送到它不是為之鑄造的主機,比方說一個 Slack webhook,代理伺服器就回 HTTP 451,一個 位元組也不轉送。VM 暫停。一列紅色的 Credential brokering 落在 Security Timeline 上,Bromure 把這個工作區標記為已遭入侵,於是你下次啟動時會有選項清掉它的 磁碟與家目錄,同時保留你的權杖、SSH 金鑰與設定。

C2 仍然得離開這台機器。出向流量是一組有序的允許與拒絕規則,由上往下比對,最後以 default allow 或 default deny 收尾。一個部署基礎設施的工作區需要 registry.terraform.io、releases.hashicorp.com 和你的雲端 API,這讓 portfolio-devs.slack.com 與某個 Arbitrum RPC 端點都不在清單上。主機會強制執行 這些規則兩次:虛擬交換器比對目的 IP 與從 DNS 側錄到的主機名,TCP 與 UDP 都比對, 代理伺服器再以 TLS 伺服器名稱比對一次。交換器把 80 埠與 443 埠導進代理伺服器, 不牽涉任何環境變數,客體也沒有任何東西可以關掉。每一次裁決都會變成 Security Timeline 上的一列 Firewall。

在負責部署的那台機器上provider 行程,以你的身分執行讀 ~/.aws → 真正的密鑰讀 git 權杖、SSH 金鑰、tfstate你的家目錄就是波及範圍外送流量,無人過問portfolio-devs.slack.com · 每 10 秒Arbitrum Sepolia RPC · 每 3 秒兩個目的地的信譽都很好沒有任何異常可以告警善後:輪換這台筆電碰得到的一切在 Bromure Agentic Coding 工作區裡provider 行程,在客體裡讀 ~/.aws → access key ID + 假密鑰Ubuntu 24.04,在 Hypervisor 之下一顆用選單項目就能換掉的磁碟外送流量,逐條比對你的規則slack.com → 無規則 → default denySepolia RPC → 無規則 → default deny而如果假密鑰被帶出範圍HTTP 451,一個位元組也不轉送,VM 暫停善後:清掉再啟動,什麼都不必輪換
同一個 provider 執行檔,兩台機器。在筆電上它以你的身分執行,從憑證鏈裡讀到一把真的密鑰,並且連得上 Slack 與一個公開的 RPC 端點,因為沒有人寫下針對它的規則。在 Bromure 工作區裡,它跑在 Hypervisor 之下的 Ubuntu 客體中,找到的是一組存取金鑰 ID 配一把假密鑰,出去的路上還會碰到一組有序的出向規則;把假密鑰帶往它不是為之鑄造的地方會拿到 HTTP 451,並讓 VM 暫停。真正該往上游走的 AWS 呼叫會在主機上重新簽章,所以 terraform 照常運作,而具破壞性的 AWS 操作則會停在一個顯示於 Mac 上的對話框。

刪除由你核准

在 provider 與你的帳號之間還有一道控制,而當攻擊者要的是你的基礎設施而非你的 機密時,那才是最關鍵的一道。

AWS 憑證帶有一份寫入政策,新工作區預設是 Prompt before write。代理伺服器依據 X-Amz-Target 標頭或 Action= 參數裡的動作名稱來分類每一次呼叫:Get*、 List* 與 Describe* 是讀取,一聲不響地放行;而 Delete*、Terminate*、 Revoke* 及其同類則算是破壞性操作。寫入會停在一個標題為 Allow write on "<scope>" from workspace "<name>"? 的對話框,上面逐字顯示該操作 而不是摘要,並列出 Allow for 15 minutes、Allow once、 Allow for the rest of the session 與 Don't allow。你拒絕,客體就收到一個 AccessDeniedException,代理會回報並繼續往下做。

按下那個對話框的人是你,在你的 Mac 上。VM 裡的行程碰不到它,Slack 頻道另一端的 那個人也碰不到。

把工具鏈當成執行器看待

terraform init 與 terraform plan 就是下載並執行。要問一個 provider 在裡面 會執行什麼,因為它真的會執行。

不要讓活的機密留在伸手可及之處

給 provider 一條能滿足它的憑證鏈,以及一把一文不值的密鑰。簽章由主機在邊界的 另一側完成,並限定在那把金鑰所屬的目的地。

先把目的地點名

一個部署用的工作區需要的是一份簡短穩定的清單:登錄中心、發行主機、你的雲端 API。寫下一次,把未命中的流量設成 Deny,死信箱合約就沒有地方可以輪詢。

讓不可逆的操作開口問

讓讀取安靜地跑。讓 Terminate* 的代價,是一個顯示在 VM 碰不到的螢幕上的 對話框。

這裡沒有人找到漏洞

HashiCorp 的登錄中心送出了別人跟它要的那個套件。Terraform 驗證了拿到的總和檢查 碼,並用它執行每一個 provider 的方式執行了這個 provider。沒有沙箱被攻破,沒有代理 讀到被污染的指令,也沒有模型出錯。

兩個加起來 1,671 次下載的 provider 觸及了至少 18 台機器,大多是 Mac,就坐在那裡每 三秒輪詢一次智慧合約。撐起整條鏈的是文件裡寫明的行為。唯一新穎的部分——一段除非 你的變數剛好取了特定名字、否則一直保持加密的酬載——存在的理由,就是不讓任何東西 找到它。

所以這些事要在工作階段開始之前就決定好,因為過程中你抓不到它們。你沒辦法掃描一個 尚未解開酬載的 provider,也沒辦法從往 Slack 的流量裡挑出外洩。把部署跑在金鑰是 誘餌、目的地是你寫下的清單、而被拿下的那台機器是你可以丟掉的地方。