[Tempest Rust 深度解讀] 一個 build.rs 就能攻破供應鏈:arrayref 惡意版本如何滲入 Rust 編譯流程 (2026-10-01)

事件背景:惡意程式藏在「編譯」而非成品裡

2026 年 8 月 20 日,Rust 套件生態發生一起典型的供應鏈入侵:熱門小型套件 arrayref 的 0.3.10 版新增了對 proc-macro1 的直接依賴,而後者的建置腳本會下載並啟動外部程式。事件原始分析可見 https://dev.to/axrisi/rust-malware-in-arrayref-how-a-buildrs-ran-a-payload-at-compile-time-e7f。

Rust 安全回應團隊證實,arrayref 0.3.10 於 07:15 UTC 發布,08:41:40 被移除,存活約 86 分鐘;同一維護者帳號下的 internment 0.8.7 與 append-only-vec 0.1.9 也受到影響。官方表示不認為原作者具有惡意,較可能是電腦或帳號憑證遭入侵,並已鎖定帳號、刪除惡意套件及恢復先前被撤回的乾淨版本。這是官方已確認的事實,而不是對維護者動機的臆測。公告見 https://blog.rust-lang.org/2026/08/20/supply-chain-attack-on-arrayref/。

攻擊鏈如何運作

第一層掩護是名稱與程式內容。proc-macro1 很像廣泛使用的 proc-macro2,發布帳號名稱也只與知名維護者 David Tolnay 的帳號相差一個字母。SafeDep 的樣本分析指出,其函式庫原始碼大致是可正常運作的 proc-macro2 複本,真正異常之處在 build.rs,以及新增的 Base64、TLS 與 HTTP 建置相依套件。換言之,單看「能否順利編譯」很難發現問題。技術拆解見 https://safedep.io/arrayref-proc-macro1-rust-build-time-malware/。

第二層是 Cargo 的正常設計。依 Cargo 官方文件,只要套件根目錄存在 build.rs,Cargo 就會先將它編譯成執行檔並執行,完成後才繼續建置套件;腳本原本用於編譯 C 函式庫、產生程式碼或偵測系統環境。官方甚至明載腳本可以執行「任意數量的工作」。文件見 https://doc.rust-lang.org/cargo/reference/build-scripts.html。

本案利用的正是這個信任邊界:arrayref 本身不必直接放入下載器,只要宣告一個非選用依賴,Cargo 就會處理該依賴及其建置腳本。SafeDep 分析顯示,Unix 版本會把下載內容寫入 /tmp/rust-setup、設定為可執行並啟動;Windows 則透過 PowerShell 與 VBScript 啟動。這些是樣本行為的分析結果,不等於每次下載套件都必然成功感染。

為何這起事件特別值得注意

攻擊者不只發布新版本,也撤回多個較新的乾淨版本。原始報導指出,Cargo 對遭撤回版本所顯示的更新提示,反而可能把使用者推向當時唯一未撤回的惡意版本。合理推論是:當攻擊者取得維護帳號後,版本管理、套件發布與警告訊號可能同時遭到操控;「看到 yank 就立即升級」因此不能再被視為無條件安全的操作。

更關鍵的是,攻擊發生在建置環境。開發者電腦與 CI runner 往往保存原始碼存取權、SSH 金鑰、套件發布權杖及雲端憑證。這些資產是否真的被竊取,官方公告沒有定論;但從權限模型推論,惡意腳本能接觸的範圍取決於執行 Cargo 的帳號擁有什麼,而不是最終程式被允許做什麼。

實際影響與不能混為一談的數字

RustSec 紀錄顯示,0.3.10 在下架前被下載 2,285 次,占同一時段 arrayref 全版本流量不到一成,原因是多數使用者仍被既有 lockfile 固定在舊版;受影響版本為 0.3.10,0.3.9 以下列為不受影響,且沒有「修補版」,因為處置方式是直接刪除惡意版本。資料見 https://github.com/rustsec/advisory-db/blob/main/crates/arrayref/RUSTSEC-2026-0260.md。

但「下載 2,285 次」不等於 2,285 台主機遭入侵:下載可能來自快取、鏡像或未完成的建置;反過來說,同一份快取也可能供多次建置使用。現有公開資料不足以給出實際受害主機數,也不足以完全確認第二階段惡意程式的所有能力。這是本案最容易被新聞標題省略的限制。

對台灣開發團隊的意義

這不是只有 Rust 維護者才需處理的問題。只要台灣團隊在後端、區塊鏈、桌面應用或基礎設施工具中使用 Cargo,風險就會落到開發機與 CI/CD。作者觀點是,防線不應只問「套件有沒有已知漏洞」,還要問「這次解析出的相依套件,會在編譯前執行什麼」。

  • 應用程式與服務應提交並審查 Cargo.lock,避免建置時無意間解析到剛發布的新版本。
  • 升級時檢查 lockfile 與 Cargo.toml 差異,特別留意新增的 build.rs、建置相依套件、網路或 TLS 函式庫。
  • CI 建置環境採最小權限、短效憑證與預設限制對外連線;機密發布工作不宜與一般相依套件建置共用同一個高權限 runner。
  • 依 Rust 官方清單檢查開發機及持久化 CI 快取。若找到相關套件,只能證明檔案曾被拉取;若確認建置曾執行,則應以可能失陷處理,保存紀錄、重建環境並輪替該帳號可讀取的憑證。

後續觀察指標

接下來值得觀察三件事:crates.io 是否為已刪除的惡意版本保留不可下載但可稽核的事件標記;Cargo 或周邊工具能否在執行建置腳本前,清楚揭露新增腳本與網路能力;企業端能否保存「當時解析到哪個版本、校驗值與 yank 狀態」的建置證據。

這起事件顯示,86 分鐘內下架套件可以縮小曝險,卻不能回答每個組織最重要的問題:當時究竟有什麼程式在誰的權限下執行。lockfile 已證明能降低自動升級造成的擴散;下一步則是把相依套件解析、建置腳本執行與憑證暴露範圍,一併納入可追溯的軟體供應鏈紀錄。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *