Needle:マルウェア自身に鍵を残した、モジュール型暗号資産窃取C2の内部構造

調査に関する注記:本調査で使用したAPIキーは、MalwareBazaarで公開配布されているマルウェアから抽出したものです。同キーは、脅威アクター自身が感染端末からのコールホーム用にエージェントへ埋め込んだ認証情報です。被害者データは、本レポートに記載した統計情報を除き一切保持しておりません。また、設定の変更も行っておりません。


概要

  • 当社のエージェント型CTIプラットフォームCaronteが自律的に分析を実施:MalwareBazaarのサンプル1件から、C2の帰属特定、被害者の列挙、オンチェーンの資金追跡まで、手動リバースエンジニアリング不要で数分以内に完了
  • Needleは現在も稼働中のMaaS型暗号資産窃取プラットフォームで、2つのモジュールが有効化されています:MetaMask・Phantom・Trust Walletを標的とするブラウザ拡張機能スプーファー、およびExodus・Trezor・Ledgerを偽装するRust製デスクトップエージェント
  • RustエージェントにはC2のAPIキーが無防備な状態で埋め込まれており、そのキーを使用して1,932件の被害者情報と、6つのブロックチェーンにまたがる攻撃者の出金設定一式を列挙しました
  • パネルのReact SPAは認証処理をすべてクライアントサイドで完結させており、localStorageに構造的に有効なトークンを書き込むだけで管理ダッシュボードが表示されました。UIルーティング層においてサーバーサイドの検証は一切行われていませんでした
  • フロントエンドバンドルには、エージェントキーと同一の認証スキームを使用した出金設定の書き込みエンドポイントが定義されています。つまり、感染端末からのコールホームに使われているものと同じ認証情報で、将来のすべての自動出金先を変更できる可能性があります

主要な調査結果

カテゴリ 詳細
サンプル Rust PE(Windows)、v1.3、SHA256: d6ca3760...a490
C2スタック React 19 SPA・Express.js API・nginx 1.29.8
有効モジュール デスクトップウォレットスプーファー・ブラウザウォレットスプーファー
被害者数 合計1,932件(ブラウザ拡張機能111件、デスクトップセッション1,821件)
標的ウォレット MetaMask・Trust・Phantom・OKX・Exodus・Trezor・Ledger・Atomic・Guarda・Zelcore
キャンペーン期間 2026年4月18日より活動開始、本稿執筆時点でも継続中
認証上の問題点 無防備なエージェントキー・クライアントサイドのみの認証・未認証のビルダー
C2インフラ 130.12.180.135(ポート3000、8080、8181)
確認された資金移動 EVMホットウォレット:約148ドル相当のETHをコールドストレージへ送金済み

発見の経緯:MalwareBazaarからC2の完全な可視化まで

Windowsの暗号資産窃取ツールとしてフラグが立てられたサンプルが、MalwareBazaarに出現しました。このサンプルを当社のエージェント型CTIプラットフォームCaronteに投入したところ、手動リバースエンジニアリングを一切必要とせず、分析パイプライン全体が自律的に実行されました。

Caronteはまずバイナリ分類から着手しました。対象はデバッグシンボルなしのRust PE(8.9MB、未署名、エントロピー値6.71)で、MalwareBazaarではRustyStealerとしてタグ付けされており、Phorpiexスパムボットネット経由で配布されていました。バイナリ内の特徴的な文字列パターンからeguiデスクトップGUIフレームワークを特定し、Rustのパニックハンドラーパスをたどってウォレット偽装ターゲット(zelcoretrezorledger)を復元、バイナリのデータセグメントから埋め込み設定スキーマを再構築しました。

静的リバースエンジニアリングでは、ウォレットスプーファー以外の機能も明らかになりました。GetAsyncKeyStateによるキーロギング(送信時だけでなく入力中のシードフレーズをキャプチャするための仕組みと一致)、WindowsのRunレジストリキーによる永続化、そしてSetUnhandledExceptionFilterやソフトウェアブレークポイントトラップを含むアンチデバッグ技術です。C2のIPアドレスはASN 202412(Omegatech LTD、アムステルダム)に解決されました。これは、分析時点でOTX上に悪性パルスが27件記録されているバレットプルーフホスティングプロバイダーとして知られています。

全分析の結果、94ノード・135エッジからなる脅威グラフが生成され、ローカルおよびMalwareBazaarのルールセットにわたる40件のYARAルールにマッチしました。その中にはキーロギング、アンチVM回避、Rustベースのスティーラーパターンに関する高重大度のヒットも含まれています。投入からわずか数分で、Caronteはオペレーターの完全なプロファイルを作成しました:プラットフォームの識別情報、C2アドレス、APIキー、そして約2,000件の被害者を管理するライブパネルです。

Caronteが自律的に生成した成果物

成果物 内容
バイナリ分類 デバッグシンボルなしのRust PE、8.9MB、RuststStealerファミリー
フレームワーク特定 egui GUI、Phorpiex配布
機能抽出 キーロギング、永続化、アンチデバッグ
埋め込み設定の復元 バイナリデータセグメントからC2 URLとAPIキーを抽出
インフラ帰属 ASN 202412 Omegatech、OTXパルス27件
脅威グラフ 94ノード、135エッジ
検出カバレッジ YARAルール40件にマッチ
情報収集にかかった時間 手動リバースエンジニアリングの数日と比較して、数分

以下のセクションはすべて、Caronteの自律的な出力をベースに行った検証と調査、すなわち人間によるレビューの過程です。


Needleとは何か

Needleはモジュール型のMaaS(Malware-as-a-Service)プラットフォームです。パネルは購入可能なモジュールを中心に構築されており、オペレーターは必要な機能のみを有効化できますが、インフラは共有されています。今回の分析中、2つのモジュールが有効化されていました。

モジュール 状態 説明
Needle Core 無効 フォームグラバー、クリッパー、システムコア
Extension Base 無効 サイト置換、バックアップドメイン管理
Desktop Wallet Spoofer 有効 Rustエージェント、偽ウォレットUI、シードフレーズ窃取
Browser Wallet Spoofer 有効 拡張機能の置き換え、パスワードとシードの傍受
Farm 無効 残高閾値によってトリガーされる遅延出金
Launch Panel 無効 トラフィック管理とランチャー作成
Captcha (Win+R) 無効 ソーシャルエンジニアリングによる配布メカニズム

このモジュール構成は、複数のオペレーターに販売またはレンタルされるプラットフォームの特徴と一致しており、無効なモジュールは現在のオペレーターが契約していない機能を示しています。この点は独立した調査によっても裏付けられています。同じ月に、Malwarebytesが別のNeedleキャンペーンを報告しており、まったく異なるインフラ(異なるハッシュ値、異なるC2 IP)を使用し、偽の取引サイトとDLLハイジャッキングによる配布に焦点を当てていました。同社のサンプルと本C2との間に重複はありません。Malwarebytesの報告が感染経路を解明したものだとすれば、本レポートはその被害者を処理するインフラの内部に迫るものです。


バイナリはデバッグシンボルなしの stripped Rust PEです。Rustのパニックハンドラーは、リリースビルドであってもソースファイルのパスを保持します。バイナリのデータセクションに存在するこれらの文字列から、ウォレット偽装ターゲットを特定できます。

src/views/zelcore/app.rs
src/views/trezor/seed_panel.rs
src/views/ledger/screens/seed_recovery.rs

バイナリにはさらに、eguiデスクトップGUIフレームワーク一式が含まれており、バイナリ内の特徴的な文字列パターンから識別できます。被害者が操作する偽ウォレットのダイアログ表示に使用されています。

バイナリのデータセグメントと設定構造の分析を通じて、Caronteはエージェントに埋め込まれた設定ブロックを再構築しました。

{
  "version": "1.3",
  "api_url": "http://130.12.180.135:3000/api/v2",
  "api_key": "alk_776...fc1",
  "open_original_wallet_on_valid_seed": true
}

open_original_wallet_on_valid_seedフィールドには注目すべき点があります。このフィールドがtrueに設定されている場合、シードフレーズの窃取に成功した後、エージェントは正規のウォレットアプリケーションを起動します。これにより被害者には本物のウォレットが正常に読み込まれたように見えるため、何かがおかしいと気づく理由がありません。設定に組み込まれた、意図的な検出回避手段です。

この設定は難読化も暗号化もされずに保存されていました。さらに重大なのは、api_keyフィールドが感染端末からC2への認証に使用されるものと同一の認証情報である点です。エージェント認証とAPIの読み取りアクセスの間に分離はなく、新規被害者を登録するキーで、被害者リスト全体を読み取ることができます。


APIサーフェスのマッピング:フロントエンドバンドルの分析

サーバーへのリクエスト送信前に、Caronteはポート3000で配信されているReact SPAのwebpackバンドルを分析しました。すべてのAPIルート文字列がバンドル済みのJavaScript内に存在していました。

/api/v2/wallets
/api/v2/antiledger/settings
/api/v2/antiledger-v2/seed-phrases
/api/v2/panel-access/evaluate
/api/v2/backup-domains/active
/api/v2/browser-spoofer/build/{buildId}/status
/api/v2/builds/{buildId}/{filename}

これにより、サーバーへの能動的な列挙を行う前に、バンドルの取得1回だけでエンドポイントの完全なマップを取得できました。


パネルへの侵入:独立した2つのアクセス層

C2の分析には、異なる種類の可視性をもたらす2つの独立した手法が必要でした。それぞれを明確に区別して説明します。

フロントエンドUI:クライアントサイドのみの認証

React SPAの認証ロジックは、ダッシュボードを表示するかログインにリダイレクトするかの判断をlocalStorageの値に基づいて行います。このルーティング判断にサーバーは関与していません。

ブラウザのlocalStorageに2つの値を直接書き込むだけで十分でした。

localStorage.setItem("auth_token", "<structurally valid JWT, exp:9999999999>");
localStorage.setItem("auth_user", JSON.stringify({ username: "admin", role: "admin" }));

アプリが認証状態を再取得しないよう/loginへのリクエストをインターセプトした上で、管理ダッシュボードの全体を表示させることができました。これはJWTライブラリのバイパスではなく、UIルーティング層におけるサーバーサイド認証ゲートの完全な不在によるものです。SPAはlocalStorageの値を表示判断において無条件に信頼していました。

この手法で得られた情報:パネルの構造、モジュールのレイアウト、設定ページ、UIの全体像です。C2スタックはHTTPレスポンスヘッダー(Server: nginx/1.29.8)とバンドルメタデータ(webpackの出力に含まれるReact 19のバージョン文字列)から確認しました。本レポートのスクリーンショットはこのフロントエンドを表示したものです。

この手法で得られなかった情報:実際のデータです。バックエンドのAPIエンドポイントは、特権操作に対して有効な署名付きJWT(HS256)を要求します。偽のトークンだけでは、SPAからのAPIコールはすべて401を返しました。

データアクセス:埋め込みエージェントキー

被害者の記録、出金設定、セッション統計はすべて、Caronteがバイナリから抽出したエージェントキーを使用した直接のAPIコールによって取得しました。このキーはエージェント向けエンドポイントを管理者JWTスキームとは独立して認証するものであり、感染端末が自身を登録するために使用するものと同一の認証情報で、被害者リスト全体とオペレーターの設定を読み取ることができました。

2つの手法は相補的ですが、完全に独立しています。フロントエンドバイパスによってパネルの構造が明らかになり、それがライブ運用中のオペレータープラットフォームであることが確認できました。エージェントキーがすべてのデータをもたらしました。


二段階の攻撃戦略

ブラウザウォレットスプーファー

ブラウザモジュールは、正規の暗号資産ウォレット拡張機能を置き換えるか傍受します。被害者は通常のMetaMaskやPhantom拡張機能を操作していると思い込みますが、偽バージョンがパスワードやシードフレーズの入力を傍受してC2へ送信します。

ポート8080で動作するビルダーは、実質的な認証なしにアクセス可能で、オペレーターは標的ウォレット、インストーラーの自己削除動作、待機モード、スマートピンのオプションを設定できます。

標的にはMetaMask、Trust Wallet、Phantom、OKX、TonKeeper、Coinbase、Atomic、Bybit、Binance Walletが含まれています。APIから記録された111件のブラウザ被害者の内訳は以下の通りです。

ウォレット 被害者数
MetaMask 48
Trust Wallet 30
Phantom 20
OKX 6
TON 3
Binance 2
Bybit 2
合計 111

監視期間中に/api/v2/walletsエンドポイントを繰り返しポーリングしたところ、数時間ごとに3〜5件の新規エントリーが増加していることが確認されました。このキャンペーンは観測期間を通じて、活発に被害者を獲得し続けていました。

デスクトップウォレットスプーファー

Rustエージェントはデスクトップウォレットアプリを偽装します。被害者がExodus、Trezor、またはLedgerのアプリと信じて起動すると、偽の「ウォレット復元」ダイアログが表示され、シードフレーズの入力を求めます。入力されたシードはC2へ送信され、有効であり自動送金モジュールが有効化されている場合、資金はオペレーターの出金アドレスへ移動します。

データ収集時点で1,821件のデスクトップセッションが記録されており、すべてのセッションでawaitingSeed: trueとなっていました。これはC2が感染端末を登録済みであるものの、シードフレーズはまだ送信されていないことを示しています。監視継続中にサーバー側の合計は1,825件に達し、新規セッションが蓄積されていることが確認されました。シードフレーズが送信されていない理由が、配布の初期段階にあるためなのか、被害者が偽ダイアログを閉じているためなのかは、データだけでは判断できません。

出金モジュールは3つの自動化モードをサポートしています:接続時の残高自動確認、パスワード入力後の自動出金、シードフレーズ送信によってトリガーされる出金の3つで、オペレーターごとに個別に切り替え可能です。


C2の内部:エージェントキーが解き明かしたもの

攻撃者の設定と資金移動

GET /api/v2/antiledger/settingsは、完全な運用設定を返しました。BTC(1PVq...qfZ)、LTC(LKmc...DBL)、DOGE(D5xE...FFq)、SOL(BpxG...YU)、TRON(TDij...UNp)、EVM(0xD5...F1b)の出金アドレスに加え、TronGrid APIキーも含まれていました。

TronGridはInfuraのTRON版にあたる、プロフェッショナルグレードのブロックチェーンRPCサービスです。ここに存在するということは、オペレーターが手動監視に頼るのではなく、出金をトリガーする前にTRON/USDTの残高確認を自動化していることを示しています。

オンチェーン分析により、この攻撃が実際に資金を動かしていることが確認されました。分析時点でEVMの出金アドレスからは約148ドル相当のETHが引き出され、3つの別々のコールドウォレットへ送金されていました。TRONアドレスには約60ドル相当のUSDTとTRXが保持されていました。BTC、LTC、DOGE、SOLのアドレスにはトランザクション履歴がなく、これらのチェーンではキャンペーンがまだ初期収集段階にあることと一致しています。

出金設定への書き込みアクセス

フロントエンドバンドルには、GETエンドポイントと同一のエージェントキー認証スキームを使用したPUT /api/v2/antiledger/settingsが定義されています。同一の認証実装が示すとおり、このキーが書き込みパスでも受け付けられる場合、6つの出金アドレスを任意のアドレスに書き換えることができ、将来のすべての自動送金の送金先をオペレーターの意図しない方向へ変更できることになります。

私たちは書き込みリクエストを一切送信しませんでした。ここにこの問題を報告する理由は、スコープ制限のないエージェントキーを配布することの完全な影響を示すためです。サンプルバイナリにアクセスできる研究者、競合他社、または敵対的な第三者は、このC2の被害者を列挙し、場合によっては収益の送金先を変更できる可能性があります。


2つのインスタンス、1つのデータベース

列挙の過程で、同一ホストのポート8181で動作する第2のパネルインスタンスを確認しました。このインスタンスも同じエージェントキーを受け付けていました。シードフレーズセッションIDをクロス参照したところ、ポート3000から照会した1,817件のうち1,814件が共通していることが確認され、ほぼ完全な重複によってデータベースバックエンドが共有されていることが裏付けられました。この差分はクエリ間の時間差によるもので、別個のデプロイメントが存在するわけではありません。

データベースを共有するこの構成は、別々のインフラを用意することなく複数のオペレーターやアフィリエイトに独立したパネルアクセスを提供するMaaSプラットフォームの特徴と一致しています。ポート8181ではブラウザウォレットのエンドポイントが空の結果セットを返しましたが、これがモジュールサブスクリプションの違いによるものか、単にそのインスタンスのAPIサーフェスが異なるためかは、入手可能なデータから確定的に判断することはできません。


侵害の痕跡(IoC)

Sample:
  SHA256: d6ca3760...a490
  MD5:    8b3433...36ca534b
  Name:   needle_agent_v1.3 (internal)
Network:
  C2 Panel:  130.12.180.135:3000
  Builder:   130.12.180.135:8080
  Secondary: 130.12.180.135:8181
  Stack:     nginx/1.29.8 (Server header), React 19, Express.js
  Hosting:   ASN 202412 Omegatech LTD, Amsterdam (bulletproof hoster)
Persistence:
  Registry:  HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
rule Needle_Crypto_Stealer_v1 {
    meta:
        description  = "Needle MaaS crypto-stealer, Rust desktop agent v1.x, distributed as agent.exe"
        author       = "Beelzebub Research"
        date         = "2026-04-28"
        reference    = "https://beelzebub.ai/blog/needle-c2-crypto-stealer-analysis"
    strings:
        // Needle-specific C2 API path and config field
        $api1 = "antiledger-v2/seed-phrases" ascii
        $api2 = "open_original_wallet_on_valid_seed" ascii
        // Rust panic handler source paths, present in release builds
        $src1 = "src/views/zelcore/app.rs" ascii
        $src2 = "src/views/trezor/seed_panel.rs" ascii
        $src3 = "src/views/ledger/screens/seed_recovery.rs" ascii
        // UI string keys from Needle's fake-wallet dialog logic
        // needle_desktop_wallet_debug.txt is a debug log path embedded in the agent
        // needle.wallet. is a namespace prefix for wallet-specific UI keys
        $ui1  = "checkseed.invalid_message" ascii
        $ui2  = "needle_desktop_wallet_debug.txt" ascii
        $ui3  = "needle.wallet." ascii
    condition:
        // Valid Windows PE (MZ header + PE signature)
        uint16(0) == 0x5A4D and
        uint32(uint32(0x3c)) == 0x4550 and
        // Rust + egui binary size range
        filesize > 5MB and filesize < 50MB and
        // Both Needle-specific API strings must be present
        all of ($api*) and
        // At least one compiled-in Rust source path
        1 of ($src*) and
        // At least two Needle UI strings
        2 of ($ui*)
}

まとめ

2週間足らずで、このNeedleの展開は2つの攻撃面にわたり1,932件のターゲットを登録しました。分析を完了する前に、EVMホットウォレットからはすでに資金がコールドストレージへ移動されており、この攻撃は仮説上のものではありませんでした。

全体像が明らかになったのは、オペレーターがエージェントを構築した方法に起因しています。自らのマルウェアに埋め込んで配布した認証情報を使用することで、私たちは被害者リストを読み取り、出金設定を抽出し、オンチェーンの活動を追跡することができました。別のNeedleキャンペーンに関するMalwarebytesのレポートは、これが孤立した展開でないことを裏付けています。複数のオペレーターが同一プラットフォーム上でキャンペーンを同時展開しているのです。


Caronteがこれを可能にした経緯

この調査全体は、サンプル1件のアップロードから始まりました。Caronteは埋め込み設定を自律的に復元し、インフラの帰属を特定し、YARAルールを生成し、脅威グラフを構築しました。上級リバースエンジニアが数日かけて行う作業です。人間のアナリストの役割は、抽出ではなく検証とオンチェーン相関分析でした。

もし貴社のSOCが今も手動でサンプルをリバースエンジニアリングしているのであれば、攻撃者の「最初の波」のスピードで動いているに過ぎません。「最後の波」には追いつけていないのです。

Caronteの仕組みを詳しく見る

翻訳元: https://beelzebub.ai/blog/needle-c2-crypto-stealer-analysis/

ソース: beelzebub.ai