Salim Bitam

PHANTOMPULSE:ハイジャック可能なブロックチェーンC2 RATの構造

Elastic Security Labsは、REF6598侵入セットを通じて暗号通貨業界の被害者に拡散された、長期間存続するRATであるPHANTOMPULSEの詳細なリバースエンジニアリング分析を発表します。

Elastic Security Labsが以前報じたREF6598では、Obsidianプラグインの悪用によってWindowsツールチェーンが侵入し、メモリ内PEローダー(PHANTOMPULL)を介して権限が昇格し、最終的にRAT(PHANTOMPULSE)で攻撃を完了する侵入セットについて詳述した。その記事は配送に焦点を当てたものでした。この記事では、最終段階である PHANTOMPULSE を分析します。PHANTOMPULSE は、3 つのプロセス注入技術を搭載し、Ethereum/Base/Optimism トランザクション入力を介して C2 を解決し、公開schuac技術を介して UAC をバイパスします。分析の結果、シンクホール化可能なブロックチェーンC2チャネル、AMSI/WLDP/ETWを無効にする統一されたハードウェアブレークポイントプリミティブ、およびインプラントのデバッグ文字列に広く存在するAI支援開発の痕跡が明らかになった。

重要なポイント

  • PHANTOMPULSEは、最近公開された攻撃的セキュリティの概念実証(PoC)から採用した3つの注入技術を実装しています。
  • AMSI、WLDP、およびETWは、単一の共有HWBPプリミティブを介してバイパスされます。
  • ブロックチェーンのC2リゾルバには送信者検証機能がないため、防御側は単一のトランザクションを送信するだけで、インプラントごとにC2 URLを上書きできてしまう。
  • バイナリーに強力なAI支援開発指標が存在する

AI支援開発に関する注記

PHANTOMPULSEには、デバッグ文字列全体にわたって、AIによるコーディング支援の強い痕跡が見られる。

最も明確な答え:

  • 操作ログの構造化されたステップ番号付け: [STEP 1] Staged mode — payload downloaded from C2 at runtime[STEP 1/3] Scheduled Task (DotNetSvcUpdateTask, logon + every 3 min)[STEP 2/3] Boot Task (DotNetSvcCoreTask, INTERACTIVE_TOKEN + BootTrigger)[UNINSTALL 4/6] Removing persist_loader DLL + registry PE data...[REPAIR] Reinstalling boot task (INTERACTIVE_TOKEN)...
  • ENTER/DONE 関数トレース: "[HEIS] encrypt_text_only ENTER" / "[HEIS] encrypt_text_only DONE""KeylogResolveAPIs: ENTER" 。LLMが新しい関数を生成する際にデフォルトで採用する診断スタイル。
  • 詳細診断: "FindHostProcessEx: scan stats: total=%lu sessSkip=%lu openFail=%lu native=%lu wow64=%lu mapReject=%lu dbgReject=%lu sess=%lu""ManualMap: thread hijacked and resumed — DLL injection via thread hijack complete" 。出力内容は自明で、マルウェアにしては珍しく饒舌だ。
  • C 文字列の Em ダッシュ: "elevate: FAIL — no deployed DLL path"">>> .elevate: NOT proxy — spawning trusted host to handle elevation"

実行チェーン

MainEntryLogic これは、C2ループに入る前に完全な初期化シーケンスを実行するオーケストレーション機能です。

start
 └─ WinMain
     └─ MainEntryLogic
         1. DynInit                // Bootstrap API resolution
         2. ElevationStateCheck    // ".elevate" marker detection, routes by token elevation state
         3. SingleInstanceCheck    // XOR-decrypted mutex, exit if already running
         4. EvasionInit            // Direct syscalls + ETW HWBP
         5. SyscallResolverInit    // CPUID + hash-based kernel32 resolution
         6. SleepMaskInit          // Sleep obfuscation setup
         7. ComputeMachineID       // DJB2(module name) ^ volume serial
         8. IsRunningHollowed      // Process hollowing self-check
         9. CollectSysInfo         // CPU, GPU, RAM, OS, AV, apps
        10. FilelessPersist        // Drop stub DLL, registry artifact
        11. InstallPersistence     // Three scheduled tasks via COM ITaskService
        12. C2Loop_Init → C2Loop_Main

起動時に、インプラントはユーザー名とコンピュータ名をDJB2ハッシュ化し、それぞれを事前に計算されたテーブルで検索します。一致する結果が出ると処理が終了します。公開されているアンチサンドボックスワードリストに対してテーブルを総当たり攻撃した結果、 61 エントリのうち 20 が回収されました。 WDAGUtilityAccount (Windows Defender Application Guard)、いくつかのDESKTOP-XXXXXXXデフォルトVM名、およびJoe SandboxペルソナabbypatexgeorgejohnlisafrankRDhJ0CNFevzX )。

防御回避

直接システムコールとAPIラッパー

PHANTOMPULSE は、DJB2 ハッシュを使用してPEB→Ldr走査することにより ntdll 関数を解決し、各 NT 関数のプロローグからシステム サービス番号 (SSN) を抽出し、プライベート システムコール スタブを構築します。これらの突起部は、インプラントの残りの部分全体で使用される上位レベルの補助装置で覆われています。

  • NtCreateFile_Wrap
  • NtWriteFile_Wrap
  • NtClose_Wrap
  • NtCreateSection_Wrap
  • NtMapViewOfSection_Wrap
  • NtProtectVirtualMemory_Wrap
  • NtWriteVirtualMemory_Wrap

インプラントの残りの部分は、 kernel32 / ntdllエクスポートの代わりにこれらのラッパーを呼び出し、EDR 製品が文書化された API サーフェスに挿入するユーザー モードntdllフック (IAT 置換、インライン迂回、またはトランポリン パッチ) を無効にします。

単一のヘルパー関数が、すべてのディスク書き込みを直接NtCreateFile + NtWriteFileにルーティングし、アクセスエラーが発生した場合は削除して再試行します。

文字列と設定の難読化

PHANTOMPULSEは、異なるアーティファクトを生成するために4つのXORレイヤーを使用します。

Key鍵がどこにあるか
C2フォールバックURL、ミューテックス、ドロップパスファイル名16バイト: F7 7C 8E 40 DF C1 7B E5 E7 4D 86 79 D5 B3 53 41埋め込まれている .rdata
ブロックチェーンプロバイダーのホスト名(UTF-16 LE)8バイト: 5A 3C 7E 1D 9F 2B 4E 8A埋め込まれている .rdata
COM 昇降モニカー、キーログファイルペイロード0xE95CA237実行時に計算され、定数を除外します .rdata計算されるものであり、保存されるものではない
ブロックチェーン取引から取得したC2 URL inputリゾルバーウォレットアドレス自体公開検索キーから再利用

AMSI、WLDP、ETW はハードウェア ブレークポイント経由でバイパスします

PHANTOMPULSEは、単一の共通プリミティブ(各APIエントリに設定されたハードウェアブレークポイントと、インラインパッチなしで戻り値を偽装するベクタ例外ハンドラによるインターセプト)を通じて、AMSI、Windowsロックダウンポリシーのコード信頼性チェック、およびETWテレメトリを無効にします。

スロット対象API偽装リターン(RAX)
DR0WldpQueryDynamicCodeTrust0S_OK
DR1AmsiScanBuffer0x80070057E_INVALIDARG
DR2EtwEventWrite0STATUS_SUCCESS

その仕組みを段階的に説明します。

  1. このインプラントは、ターゲットAPIの問題を解決します。AMSIとWLDPはLoadLibraryA +ハッシュベースのエクスポートルックアップを実行します。ETWはntdllが既にロードされているため、PEB→Ldrウォークを使用します。
  2. HWBP記述子(ターゲットAPIアドレス、モード、偽装された戻り値)は、グローバルスロットテーブル内の4つの40バイトスロットのいずれかに書き込まれます。
  3. ヘルパースレッドはターゲットスレッドを一時停止し、 NtGetContextThread / NtSetContextThreadを呼び出して DR0–DR3 + DR7 を書き込み、その後再開します。(インプラントのベクトル例外ハンドラが既にインストールされている場合は、代わりにプロセス内例外STATUS_BREAKPOINTが発生し、VEH がヘルパースレッドなしでスロットテーブルを読み取り、DR をプログラムできるようになります。)
  4. 保護されたAPIが呼び出されると、CPUは関数の最初の命令でDebug Exceptionを発生させます。
  5. インプラントのベクトル例外ハンドラはDebug Exceptionをインターセプトし、4 スロットのテーブルを走査して発火アドレスを見つけ、スレッドコンテキストを変更します。 CONTEXT.Raxはスロットごとに偽装された戻り値に設定され、 CONTEXT.Ripは呼び出し元に戻る事前格納された「スキップ」サンクにリダイレクトされます。
  6. ハンドラはEXCEPTION_CONTINUE_EXECUTIONを返します。呼び出し側は、偽装されたRAXを、あたかもAPIが実行されたかのように認識する。

ディスパッチャーは2つの経路に対応している。1 つのハンドラ ( VEH_Dispatcher ) は、インプラント自身のRaiseException(STATUS_BREAKPOINT)呼び出し (スロット テーブルから DR レジスタをシードして再プログラムするために使用されます) と、保護された API が呼び出されたときに発生するSTATUS_SINGLE_STEP例外の両方を処理します。例外コードによって分岐が決まります。 STATUS_BREAKPOINT DR プログラミングをトリガーし、 STATUS_SINGLE_STEPスプーフィングをトリガーします。

ハンドラーも直接登録されていません。AddVectoredExceptionHandler 、実行時に新しいMEM_PRIVATEページ ( VirtualAlloc + VirtualProtectPAGE_EXECUTE_READ ) に割り当てられた小さな JMP サンクを受け取ります。サンクは、 JMP [RIP-relative]間接ジャンプ(6バイトオペコードFF 25 00 00 00 00 )の後にディスパッチャのアドレスがインラインで続くものです。AmsiScanBufferWldpQueryDynamicCodeTrustEtwEventWriteにはバイトが書き込まれることがないため、プロローグパッチをスキャンするシグネチャベースの検出ではこれを完全に見逃します。

ビルドバリアント:アクティブサブシステムと休止サブシステム

バイナリファイル内には、コードまたは文字列として存在するサブシステムがいくつかありますが、このビルドではアクティブになっていません。これは、より大規模なコードベースから簡略化したビルドです。

  • NTDLL のフック解除: フック解除サブシステムのデバッグ文字列は.rdata ( UnhookNtdll: ntdll base = %papplied %d relocation fixups to .text ) にありますが、それらを参照するものはありません。この変異型では死亡している。
  • レジストリ常駐型PEブロブローダー:以前のビルドでは、次のステージのPEをレジストリ内に保存していました。このビルドではそうではありませんが、アンインストールルーチンは依然として従来のレジストリブロブをクリーンアップします。
  • COMハイジャックの永続化:このビルドではインストールされていません。クリーンアップ処理はアンインストールルーチン内に残されています。
  • 印刷モニターの永続性:COMハイジャックと同じパターン。インストールパスは削除され、アンインストールパスは保持される。

最後の3つ(レジストリブロブローダー、COMハイジャック、プリントモニター)は、これとは逆のパターンを示しています。インストールロジックはなく、クリーンアップロジックのみが残されており、古いデプロイメントとの下位互換性のために保持されています。

機能ペイロードの構築エビデンス
直接システムコール(SSN抽出)アクティブSSN抽出+スタブ生成を確認済み
AMSI / WLDP / ETW HWBPバイパスアクティブDR0/ DR1 / DR2共有ヘルパースレッドプリミティブ経由
三方向プロセス注入アクティブPhantomInjectDbgNexumManualMapはすべて機能的です
ブロックチェーンC2の解決アクティブBlockscoutのプロバイダー3社に問い合わせた
NTDLLのフック解除デッドコード文字列は存在するが、コード参照はゼロである。
HEIS暗号化無効コードの暗号化/復号化スタブ
レジストリ常駐型PEブロブローダーレガシーのみアンインストール時のみクリーンアップ
COMハイジャック永続性レガシーのみアンインストール時にクリーンアップされ、インストールはされませんでした。
印刷モニターの永続性レガシーのみアンインストール時にクリーンアップされ、インストールはされませんでした。
偽物の紐アクティブ参照されていないデコイ文字列が 4 つあります .rdata

コマンド&コントロール

ブロックチェーンC2の解決

PHANTOMPULSEは、3つのBlockscoutプロバイダーを通じてC2ルックアップを分散化します。

  • eth.blockscout[.]com (イーサリアムL1)
  • base.blockscout[.]com (ベースL2)
  • optimism.blockscout[.]com (楽観主義 L2)

ウォレットアドレス0xc117688c530b660e15085bF3A2B664117d8672aAは、16バイトの鍵を使用してストレージからXOR復号化されます。各プロバイダーに対して、インプラントはHTTPS GET(ポート443、SSL証明書エラーは無視)を発行し、最新のトランザクションのinputフィールドを取得し、それを16進数でデコードし、ウォレットアドレスのバイトをキーとしてXOR復号化し、結果がhttpで始まることを検証します。完全に失敗した場合は、ハードコードされた URL https://panel.fefea22134[.]netにフォールバックします。

リゾルバはトランザクションの送信者を検証しません。これは、最新のデコードされたinput httpで始まることだけをチェックします。誰でも、ウォレットバイトの下にXORエンコードされた独自のURLを使用して、そのウォレットにトランザクションを送信できます。その後、そのキャンペーンのPHANTOMPULSEインスタンスがポーリングを行うと、そのURLに解決されます。ネットワーク防御側にとって、これは1回のトランザクションを犠牲にするだけで有効なシンクホールとなる。

エンドポイントと心拍数

実行時に5つのAPIパスが構築され、セッションごとのキーを使用してメモリ内で再暗号化されます。

パスメソドコンテンツタイプ目的
/v1/telemetry/report役職application/jsonフルシステムテレメトリによるハートビート
/v1/telemetry/tasks/<machine_id>Getコマンドフェッチ
/v1/telemetry/upload/役職image/bmpスクリーンショット/ファイルアップロード
/v1/telemetry/result役職application/json指揮官の成果達成
/v1/telemetry/keylog/役職text/plainキーログデータのアップロード

ハートビートは、完全なシステムプロファイルをJSON形式で送信します。

{
  "machine_id": "<uint32>",
  "status": "online",
  "cpu": "<model>",
  "gpu": "<description>",
  "ram_mb": "<uint32>",
  "os": "<version>",
  "username": "<user>",
  "computer_name": "<name>",
  "cores": "<uint32>",
  "screen_w": "<int>",
  "screen_h": "<int>",
  "privilege": "<user|admin|admin_nouac|system>",
  "build": "payloads",
  "public_ip": "<ip>",
  "av_list": ["<av1>", "<av2>"],
  "apps": ["<app1>", "<app2>"],
  "last_cmd": "<cmd>",
  "last_cmd_result": "<result>"
}

2 つの応答フィールドが解析されます: "status":"deleted"完全なアンインストールをトリガーします。 "ip":"<value>"パブリック IP キャッシュを生成しますが、ローカル検出 (ipif[.]org) の場合に限ります。/icanhazip[.]com/checkip.amazonaws[.]com)まだ記入されていません。

ループのリズムと回復力

  • 睡眠時間:[20, 40]秒のランダムな時間間隔
  • 自己修復:2回目の反復で実行され、その後10回目の反復ごとに実行されます。
  • 健康状態監視ティック:インプラントがオンラインになった後の最初の呼び出し、その後は5回ごとの反復ごとに呼び出されます。ローカルのシステム情報構造体(CPU使用率、RAM容量、OSバージョン、稼働時間、コンピュータ名)にデータを入力します。
  • 失敗しきい値: 10 回の連続したハートビート失敗で、スタックしたSSL/TLSリカバリのために自己再起動がトリガーされます
  • 再解決:失敗した場合、ブロックチェーンの再解決が実行されます。解決されたURLが変更された場合、失敗カウンターはリセットされます。
  • 公開IPアドレスapi4.ipify[.]orgipv4.icanhazip[.]comcheckip.amazonaws[.]com
  • 接続チェック:プローブmicrosoft[.]comgoogle[.]comcloudflare[.]comgithub[.]com

指揮指令

コマンドディスパッチャは、DJB2ハッシュを使用してコマンドをルーティングします。合計8つのコマンド:

ハッシュCOMMAND行動
0x04CF1142injectシェルコード/DLL/EXEを注入する。タイプ別のルート: シェルコード → PhantomInject ; DLL → ManualMap ; EXE → DbgNexum 。AMSIとWLDP HWBPバイパスは最初のinject呼び出しでインストールされます(ETW HWBPはEvasionInitから既に設定されています)。
0x7C95D91Adropファイルをディスクにドロップして実行してください。DLL、EXE、シェルコード(APCインジェクション)、およびMSIペイロードをサポートします。
0x9A37F083screenshotGDIキャプチャを行い、幅960ピクセルに縮小し、BMP形式でアップロードする。
0x08DEDEF0keylogインラインキーロガーを開始または停止します。
0x4EE251FFuninstall6段階のクリーンアップと駆除。
0x65CCC50Belevateschuac技術による UAC バイパス ( IElevatedFactoryServer::ServerCreateElevatedObject(CLSID_TaskScheduler) ); インプラントを再起動する一時的な高負荷タスクを登録します。
0xB3B5B880downgradeシステム → 管理者権限の昇格。
0x20CE3BC8(自己再起動)カスケード自己終了: NtTerminateProcess(-1, 0)最初に直接システムコールを実行します。解決に失敗した場合は、 ExitProcess(0)にフォールバックします。パーシステンスは、次のスケジュールされたタスクティックでインプラントを再起動します。ソフトリスタートと動作的に同等。

8番目のハンドラには、それを示すデバッグログがありません。自動的に終了し、スケジュールされたタスクによって次のティックでインプラントが再起動されます。LLMスタイルの足場(デバッグ文字列)がないことから、これはバイナリ内の数少ないハンドラの1つであり、LLMによって生成されたものではなく、人間が追加したものと思われる。

注射技術

PHANTOMPULSEは、ペイロードの種類ごとに1つずつ、3種類の注入技術を搭載しています。inject C2 コマンドは、シェルコードをPhantomInjectに、DLL をManualMapに、EXE をDbgNexumにルーティングします。

AMSI/WLDP ハードウェアブレークポイントのバイパスは、インジェクターが実行される前に、最初のinject呼び出し時にインストールされます。

ペイロードのタイプインジェクター戦略
シェルコードファントムインジェクトSEC_IMAGEを介してdbghelp.dllでモジュールを踏みつける
EXEDbgNexumDebug-API ステートマシン
DLLManualMap完全なPEマニュアルマッピング

PhantomInject: モジュールがdbghelp.dllに侵入しています

モジュールストンピングは、正当なWindows DLLをSEC_IMAGEとしてマッピングし、 .textを上書きすることで、 MEM_PRIVATE割り当てを回避します。

  1. SeDebugPrivilegeを取得し( OpenProcessToken / LookupPrivilegeValueW / AdjustTokenPrivileges経由)、次に 7 つのホストプロセス候補のうちの 1 つについてプロセス スナップショットを走査します(大文字小文字を区別しない一致)。優先順位順に試行: sihost.exetaskhostw.exebackgroundTaskHost.exeRuntimeBroker.exedllhost.exectfmon.exeexplorer.exe

  2. NtOpenFileを介してdbghelp.dllを開き、 SEC_IMAGEセクションを作成し、 NtMapViewOfSectionを介してターゲットにマッピングします。

  3. ローカルコピーを解析して.text RVAとサイズを取得し、その後解放します。

  4. スレッドを選択して一時停止し、コンテキストを取得します

  5. 82バイトの保存・呼び出し・復元トランポリンを構築します

  6. マップされたDLLの.textにシェルコードとトランポリンを書き込みます。

  7. 保護を反転 PAGE_EXECUTE_READ

  8. トランポリンにRIPをリポイントし、スレッドを再開します

メモリスキャナから見ると、結果は、正しいファイルパス、セクション名、および最初のページハッシュを持つ、ファイルベースのイメージ領域である正当なdbghelp.dll内で実行されているスレッドのように見えます。

DbgNexum: デバッグAPIを実装コントローラーとして使用する

DbgNexumはEXEペイロードを処理します。実行可能なコードを事前にターゲットに書き込むのではなく、WindowsのデバッグAPIを使用して、例外を一つずつ処理しながら実行を進めます。これは、ターゲット内の完全なWindows APIをガジェットとするROPチェーンです。

この技術はPHANTOMPULSE独自のものではありません。これは、2026年1月4日にGitHubで公開された概念実証であるdis0rder0x00/DbgNexumをそのまま転載したものです。オペレーターは、インプラントのデバッグ文字列内の公開されたテクニック名( "DbgNexum" )を変更せずに保持し、内部ステートマシンは1対1で一致しています。同じベイトAPI、セクション名、ガジェットチェーン、定数です。PHANTOMPULSE は、PoC にはない運用上の足場を備えたリフトされた x64 コアをラップします。ホスト プロセスの選択 ( FindHostProcessEx )、新しいSysWOW64\cmd.exe / rundll32.exe / notepad.exeを生成するフォールバック、カスタム PE ローディング ブートストラップ (生のシェル コードの代わりに完全な EXE を運ぶことができるように)、および別の WoW64 クロス アーキテクチャ バリアント。

ネイティブx64ペイロードの場合、インプラントはPE、ブートストラップスタブ、およびトランポリン構成を名前付きファイルマッピングセクション内に事前配置します。セクション名はリテラルな2バイト文字列"MZ"であり、インプラントはDebugActiveProcessでアタッチされ、DR0にハードウェアブレークポイントを持つリモートスレッドをFileTimeToSystemTime上に作成します。

餌がブレークポイントに到達すると、ステートマシンがターゲットをこのAPIチェーンを通して駆動します。

  1. トラップフラグを設定した状態でRIP DbgBreakPoint+1にリダイレクトします。結果として発生する単一ステップ例外は、チェーンの残りの部分に橋渡しされます。
  2. LocalAlloc(LMEM_ZEROINIT, 3)3バイトの名前バッファを割り当てます。
  3. memcpy(buf, kernel32_base, 2)kernel32.dllの DOS ヘッダーから"MZ"バッファにコピーします。
  4. memset(stack+40, 0, 8): スタック引数スロットをゼロにする。
  5. OpenFileMappingA(0x1F, FALSE, "MZ")準備済みのセクションを、セクションマッピングへのフルアクセス権限で開きます。
  6. MapViewOfFile(...)それをターゲットにマッピングします。
  7. RIPをブートストラップスタブであるmapped_base + 0x400にリダイレクトします。これは直接ログに記録される唯一のステージです: DbgNexumLoop64: stage 6 -> stub at %llx, base=%llx 。(PoCは生のシェルコードを表示するためにmapped_base + 0にリダイレクトします。PHANTOMPULSEは独自のPEローダーに着地するために+0x400オフセットを追加します。)

各遷移は次のデバッグイベントをインターセプトし、 RSPを復元し、トラップフラグをクリアし、 RIPと引数レジスタ ( RCXRDXR8R9 ) を次の呼び出しのために変更し、デバッグ対象を続行します。DR0 は保存された各リターン アドレスで実行ハードウェア ブレークポイントとして再利用されるため、ターゲットに対するインライン パッチやWriteProcessMemoryは必要ありません。カーネルの視点から見ると、 kernel32.dll内のスレッドがLocalAllocOpenFileMappingAMapViewOfFileを呼び出しただけです。

クロスアーキテクチャパス(64ビットインプラントからのPE32)は、公開されているPoCには含まれていないPHANTOMPULSE専用のバリアントです。ショートカットを使用します。インプラントはターゲットのPEB.LdrNtReadVirtualMemory経由で走査し (32 ビットまたは 64 ビットの PEB レイアウトを選択するためにProcessWow64Informationを使用)、呼び出し可能なエントリ ポイントを持つ DLL を見つけ、次に 32 ビット ローダー スタブとトランポリンを含むセクションをNtCreateSection + NtMapViewOfSection経由で両方のプロセスに事前マッピングします。リダイレクトは2つの段階から成ります。 RtlExitUserThreadで餌を仕掛け、次にDbgBreakPointを介した1ステップジャンプでRIPからトランポリンへ移動します。このパスにはAPIチェーンはありません。セクションは既に両側でマッピングされているため、 OpenFileMappingA / MapViewOfFile必要ありません。

クロスアーキテクチャのホスト選択はFindHostProcessExで実行され、重要なシステムプロセス ( csrss.exelsass.exesmss.exewinlogon.exeservices.exe 、 、 wininit.exeMsMpEng.exe ) を除外し、使用可能な WoW64 ホストが見つからない場合は、新しいSysWOW64\cmd.exe / svchost.exe / notepad.exe rundll32.exe生成するようにフォールバックします。

ManualMap: フルPEマッパー

ManualMapは、完全なPE手動マッピング実装によりDLLペイロードを処理します。

  1. MZ/PE署名を検証します。x64ホストパス内のPE32を拒否します(デバッグログ: "PE32 DLL in x64 host is impossible" )。

  2. NtAllocateVirtualMemoryを介してターゲットにSizeOfImage割り当てます

  3. ヘッダーとセクションをローカルのステージングバッファにコピーします。

  4. ベース再配置( IMAGE_REL_BASED_DIR64IMAGE_REL_BASED_HIGHLOW )を適用します

  5. LoadLibraryA + GetProcAddressを介してインポートを解決します

  6. PEヘッダーを消去します( SizeOfHeadersバイトをゼロで埋めます)

  7. ステージングされたイメージをリモート割り当て領域に書き込みます

  8. セクションごとのメモリ保護を設定します

  9. 0x2000バイトの別のリモート割り当て領域( PAGE_EXECUTE_READに設定)に137バイトのトランポリンを構築します。

完全なトランポリンシェルコードのgistには、完全なバイト列が含まれています。

10. サスペンド/get-context/set-context を介してスレッドを乗っ取る

権限昇格

elevateコマンドは、 schuacテクニック( IElevatedFactoryServer::ServerCreateElevatedObject(CLSID_TaskScheduler) ) による UAC バイパスであり、zcgonvh によってUACME 問題 #129として公開され、現在は ID 74 で管理されています。

機構

MaintenanceUI.dll's CMaintenanceUIVirtualFactory (CLSID {A6BFEA43-501F-456F-A845-983D3AD7B8F0} ) はElevationレジストリ キーで登録されているため、OS は管理者以外の呼び出し者に昇格されたインスタンスを渡します。そのIElevatedFactoryServerインターフェースはServerCreateElevatedObject(rclsid, riid, ppv)を公開しており、昇格されたサーバーはこれを使用して、昇格されたコンテキストで他のCLSIDをインスタンス化します。PHANTOMPULSEはCLSID_TaskSchedulerデータを送り、レベルが上昇したITaskServiceを受け取り、そのサービスを使用してインプラントを再起動するHighestAvailable -RunLevelタスクを登録します。

elevate C2コマンド

ProcessCommands内部では、昇格ハンドラが実行されます。

  1. タスクアクションを作成します。コマンドは<system_dir>\rundll32.exeで、引数は\"<deployed_dll>\",DllRegisterServerです。ユーザー識別子はGetEnvironmentVariableWからCOMPUTERNAME\USERNAMEです。![昇格コマンド呼び出し][/assets/images/blockchain-c2-phantompulse-rat-sinkhole/image17.png]
  2. .elevateマーカーをエンコードされたパラメータではなく、単一バイト ( "1"0x31 ) として書き込みます。書き込みはNtCreateFile + NtWriteFileを経由してユーザーモードのフックをバイパスします。マーカーは単なる存在フラグであり、標高パラメータはインプラントが登録しようとしているタスク定義の中に含まれます。
  3. COM 昇格モニカーをランタイムで XOR 復号します。 .rdataの 66 バイトをシード0xE95CA237と XOR 演算し、 Elevation:Administrator!new:{A6BFEA43-501F-456F-A845-983D3AD7B8F0}に復号します。
  4. CoGetObject(moniker, &BIND_OPTS3{dwClassContext=CLSCTX_LOCAL_SERVER}, IID_IElevatedFactoryServer, &factory)を呼び出して昇格されたIElevatedFactoryServer*を取得し、次にfactory->ServerCreateElevatedObject(CLSID_TaskScheduler, IID_ITaskService, &elevatedTaskService)呼び出して昇格されたITaskService*を取得し、その昇格を継承します。
  5. 上記のrundll32アクションを使用して、 HighestAvailableランレベルで一時的なタスクDotNetSvcElevateTaskを登録します。
  6. 既存の非昇格永続タスクをすべて削除し、古い低IL永続化プロセスが昇格再起動と競合しないようにします。
  7. 一時タスクに対してITaskService::Runを呼び出し、直後にそれを削除します。単一インスタンスのミューテックスを解放し、中程度のILインプラントから退出します。

プロキシ経由でフォールバック再試行 rundll32

CoGetObject / RegisterTaskチェーンが失敗した場合、フォールバックパスが引き継がれます。別の起動パスでは、 CreateProcessWを介して直接新しいrundll32.exe "<deployed_dll>",DllRegisterServerが起動され、再試行 ( ">>> .elevate redirect attempt %d" ) が行われます。rundll32 の内部では、インプラントがisProxy=1elevated=0を検出し、3 つの登録バリアント ( ELEVATED+INTERACTIVE+userELEVATED+INTERACTIVEINTERACTIVE ) を使用してschuac シーケンスを再試行します。成功すると、上位タスクが起動し、上位再起動が引き継がれる。処理が尽きると、プロキシは">>> Phase 1: all registration methods failed, cleaning marker"ログに記録して終了します。

昇格されたrundll32の再起動

一時的なタスクが実行されると、 svchost.exe (Schedule)新しい高 IL トークンを使用してrundll32.exe "<deployed_dll>",DllRegisterServerを起動します。インプラントのDllRegisterServerエクスポートはエントリとして実行されます。起動時に.elevateマーカーと高ILトークンを検出し、昇格されたパスにルーティングします。

  • .elevateマーカーを読み取り、削除します
  • 管理者権限で永続性を再インストールします。これには\Microsoft\Windows\NetFramework\の下のブートタスクDotNetSvcCoreTaskが含まれ、 INTERACTIVE_TOKEN + BootTriggerに登録されます。登録には管理者が必要です。
  • 高ILサービスとして、通常のインプラント操作を継続します。

マーカー状態ルーティング

起動のたびに、 MainEntryLogic .elevateパス上のGetFileAttributesWを呼び出します。INVALID_FILE_ATTRIBUTESが返された場合、インプラントはすべての昇格ロジックをスキップして通常どおり起動します。マーカーが存在する場合、インプラントはさらに2つの事実を収集します。現在のプロセスがrundll32.exe / regsvr32.exeプロキシであるかどうか、およびトークンが昇格されているかどうかです。そして、その組み合わせに基づいてルーティングを行います。

マーカープロキシ高台動作
不在n/an/a通常起動、特別なルーティングなし
現在いいえいいえプロキシ rundll32 を起動して schuac チェーンを再試行し、その後終了します。
現在はいいいえrundll32 内で schuac を再試行します。再試行が失敗した場合は、マーカーを削除して終了します。
現在*はいマーカーを削除し、ブートタスクで永続性を再インストールし、高ILとして続行します

マーカーファイルの内容は読み込まれることはなく、その存在自体がルーティングを決定する。

Elastic Security Labs の「 Exploring Windows UAC Bypasses」では、 IElevatedFactoryServerクラスのバイパスの検出パターンを直接扱っています。

永続化

予定されている3つのタスク

PHANTOMPULSE は COM ITaskServiceインターフェイスを介して 3 つのスケジュール済みタスクをインストールし、それぞれがrundll32.exe "<stub_dll>",DllRegisterServerを実行します。

タスクトリガー間隔ランレベル
DotNetSvcUpdateTaskユーザーログオン+時間3分STANDARD
DotNetSvcCoreTaskブート+時間15分最高利用可能 + 非表示
DotNetSvcUserTaskユーザーログインログイン時STANDARD

ブートタスクは、正当な.NETメンテナンスタスクと混在するように、 \Microsoft\Windows\NetFramework\の下に配置されます。その完全な XML 本体は.rdataに埋め込まれており、実行時に構築されるわけではありません。この文字通りの塊に刻まれた署名は、ビルドを超えて永続的に保持されます。

インストールでは、タスクごとに複数のRegisterTaskフォールバックバリアントを試行し、 INTERACTIVE_TOKENS4Uログオンタイプを異なるユーザー識別子形式( COMPUTER\UserSID 、短いユーザー名)と組み合わせます。試行の正確な順序は、インプラントの権限コンテキスト(SYSTEM、管理者、または標準ユーザー)によって異なります。失敗した場合は、成功するまで次の試行が繰り返されます。

スタブDLLのドロップ

デプロイされたエージェントは、EXE 自身の.rdata内に暗号化および圧縮されて存在します。FilelessPersist必要に応じて復号化し、( NtCreateFile + NtWriteFile ) を介して結果をディスクに書き込みます。

復号化は2つの段階から成ります。

  1. ( decoded[i] = blob[i] ^ key[i & 0xF]decoded[0] = 0x1Eは LZNT1 チャンク ヘッダーを初期化するハードコードされた最初のバイト) で、回転する 16 バイトのキーに対してブロブを XOR デコードします
  2. デコードされたバッファ上のRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, ...)はDLL( svcagent.dll )です。

優先順位に従ってパスを削除し、アクセスエラーが発生した場合はそのまま処理を継続します。

  1. %ProgramData%\AssetMon\svcagent.dll (主要な)
  2. %APPDATA%\AssetMon\svcagent.dll
  3. %TEMP%\svcagent.dll
  4. 別のパスにある冗長な%ProgramData% 「スリーパー」コピー

アナリストは、上記の 2 つの領域を EXE から読み込み、XOR ループを適用し、その結果をRtlDecompressBuffer (または任意の LZNT1 実装) に渡すことで、展開された DLL をオフラインで再現できます。これは、以下の CyberChef のスクリーンショットに示されています。

DLLサイドロード移行

SetupRegistryPE内のブロック ( MigrateSideload / MigrateLegacySideloadsデバッグ文字列のプレフィックスでログに記録されます) は、実行中のプロセスとその実行可能ディレクトリを列挙し、 diagcore.dllを探します。見つかった場合、 CopyFileWを介して現在のスタブでファイルを上書きします。

自己回復

自己修復は、C2ループのイテレーション 2 と、それ以降の10イテレーションごとに実行され、遅延永続フラグがクリアされていることを条件とします。チェック順序:

  1. まずレジストリの永続性チェックを行います。 CheckRegistryPersistence ブロックの最上部を走っている。異常を報告した場合、インプラントは直ちにFilelessPersist (スタブDLLの再復号と再ドロップ)とInstallPersistence (タスクトリガーの再登録)を再実行します。
  2. タスクの検証。 SelfHealCheckTasks 次に、3 つの永続化タスク ( DotNetSvcUpdateTaskDotNetSvcCoreTaskDotNetSvcUserTask ) を確認し、不足しているタスクがあれば再インストールします。ブートタスクのチェックはSYSTEMまたはadminのコンテキストでのみ実行可能であり、特権を持たない呼び出し元はこれをスキップします。
  3. AV機器の在庫情報を更新します。 DetectInstalledAV 最後に実行され、オペレーターが閲覧できるAV製品リストを更新します。

特権の制限は、立ち退きにおいて重要となる。管理者権限のないコンテキストでは、ブートタスクのチェックがスキップされるため、クリーンアップ中にブートタスクは検査されません。完全な削除を行うには、管理者権限で実行されている環境から、3つのタスクとレジストリアーティファクトすべてを1つのウィンドウで削除する必要があります。

反復ベースの自己修復機能に加えて、このインプラントは単一のフラグをキーとする遅延持続メカニズムを備えている。C2Loop_Mainのハートビート成功パスでは、フラグが設定された状態でハートビート成功カウンターが 1 を超えると、インプラントはFilelessPersist + InstallPersistenceを再実行し、フラグをクリアします。これにより、PHANTOMPULSEは、反復ベースの自己修復とは異なるトリガーで起動する、2つ目の永続性修復パスを持つことになります。

収集

クリップボード監視機能を備えたインラインキーロガー

キーロガーは専用のスレッドを使用せず、C2ループ内でインラインで実行されます。実行時にuser32.dllからAPIを解決します。

API目的
GetAsyncKeyState世論調査の鍵となる州
GetForegroundWindowアクティブウィンドウ検出
GetWindowTextAウィンドウタイトルのキャプチャ
MapVirtualKeyA / ToUnicode主要翻訳
GetClipboardSequenceNumberクリップボードの変更検出
OpenClipboard / GetClipboardDataクリップボード読み取り(CF_UNICODETEXT)

ログファイルは0xE95CA237シードでXOR暗号化されています。アップロード時には、再送信を避けるため、差分データのみが送信されます。

スクリーンショット

スクリーンショットは、ハッシュによって解決されるGDI APIを使用しています。デスクトップの幅が 960 ピクセルを超える場合、画像はアップロード前に縮小されます。生の BMP はメモリ上に構築され、 Content-Type: image/bmpを使用してアップロードされます。screenshot C2 コマンド (ハッシュ0x9A37F083 ) によってオンデマンドでトリガーされます。

システム偵察

インプラントが収集する偵察データ:

データ送信元
CPU登録: ProcessorNameString
GPUレジストリ表示アダプターDriverDesc (フィルター "Microsoft Basic")
ラムGlobalMemoryStatusEx
OSRtlGetVersion ビルドとバージョンのマッピング付き(Windows 7~Windows 11、Server 2008 ~Server 2025)
ユーザー名GetUserNameW explorer.exeトークンからLookupAccountSidWへのフォールバックあり
権限トークン昇格タイプ: useradminadmin_nouacsystem
AVDetectInstalledAV 実行中のプロセスを、ハードコードされた約25~30個のAVベンダーのプロセス名のリストと照合します。
アプリDetectInstalledApps 厳選された19個の対象アプリのリストをチェックします
ファイアウォールの状態プロファイルごとの有効状態を記録するためにSYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\{Domain,Standard,Public}Profileを読み取ります
サービスサービス列挙による実行中のサービス数
マシンIDDJB2(モジュール名)^ボリュームシリアル
パブリックIPマルチAPI HTTPSチェーン

AV検出リストは非常に幅広く、Defender、Norton、McAfee、Avast、AVG、Avira、Bitdefender、ESET、F-Secure、G Data、Kaspersky、Panda、Sophos、Trend Micro、VIPRE、Webroot、ZoneAlarm、Comodoといった欧米の一般的な消費者向けAV製品に加え、EDRベンダー(CrowdStrike、SentinelOne、Cylance、Malwarebytes、HitmanPro)もすべて網羅しています。このインプラントは、 AhnLab V3 (韓国)、 Qihoo 360 / 360 Total SecurityおよびTencent QQPC (中国)、 K7 Computing (インド)も検出します。アジアAVの組み込みは、欧米を標的とした商品窃盗犯としては異例であり、複数の地域市場の被害者を対象としたインプラント設計と一致する。

また、インプラントは、名前で厳選された 19 高価値アプリケーションのリストをチェックし、ハートビート( App detection: found %d apps )で一致するものを検出します。

カテゴリーターゲット
暗号通貨ウォレットledger, trezor, bitcoin-core, electrum, exodus, atomic, guarda
メッセンジャーtelegram, discord, signal, viber, slack, whatsapp
メールクライアントthunderbird, outlook
2FAアプリauthy
ファイル転送 / SSHfilezilla, winscp
ゲームsteam

検出機能( DetectInstalledApps )はレジストリをスキャンしたり、プロセスを列挙したりしません。3 つの環境変数ルート ( %LOCALAPPDATA%%APPDATA%%ProgramFiles(x86)% ) を展開し、アプリごとにハードコードされた UTF-16 相対パスサフィックスを連結します (例:\Telegram Desktop\\Authy Desktop\\Ledger Live\\@trezor\trezor-suite\\Steam\steam.exe )、そして各パスでGetFileAttributesW呼び出します。エラー以外の応答が返された場合、アプリはインストールされており、その名前はハートビート結果バッファに記録されています。

PHANTOMPULSE自体は、これらのいずれからもデータを抽出しません。このリストは、後続任務のための目標偵察情報です。オペレーターはハートビートで、特定の被害者がどの高価値アプリケーションを持っているかを確認し、 injectまたはdropを介して次にどの特殊なペイロードを送信するかを決定します。

分析対象サンプルには、ウォレット、ブラウザ、メッセンジャー、または認証情報窃盗機能は確認されませんでした。ターゲットリストは、オペレーターの意思決定ツリーに情報を提供する、単なる存在確認のためのものです。

アンインストール

uninstallコマンド、ハートビート応答の"status":"deleted" 、またはレジストリの kill フラグによってトリガーされる 6 段階のクリーンアップ:

ステップアクション
1/6HKCUとHKLMにkillフラグを書き込み、ホストプロセスを強制終了する。
2/6COM + CreateProcessWフォールバック経由でスケジュールされたタスクをすべて削除します 3 個
3/6レガシーレジストリの削除: NTLoad 値、COM ハイジャックキー、プリントモニターキー
4/6スタブDLL、スリーパーログ、レジストリPEブロブ、ProgramDataディレクトリを削除します。
5/6ディスクからインストールパスと自己パスを削除します。
6/6残存するhealthmon.exesvcagent.dllをホストしているrundll32.exeインスタンスを終了してください

ステップ 3 、このビルドではインストールされないCOMハイジャックとプリントモニターキーのクリーンアップロジックなど、従来の永続化技術が明らかになります。

帰属

PHANTOMPULSEの攻撃手法、標的設定、およびインフラの選択は、Lazarus、BlueNoroff、UNC5342(Contagious Interview)、およびAPT38を含む、北朝鮮と連携する暗号標的型侵入グループと一致している。複数の独立した側面が、これらのクラスターに関する最近の公表報道と一致している。

北朝鮮の報道と一致する兆候:

  • トランザクションinputフィールドを介したブロックチェーン解決C2は、 Mandiantが「北朝鮮がイーサリアム隠蔽を採用」におけるUNC5342(伝染性インタビュー)に起因すると考えるデッドドロップ解決パターンに一致する。PHANTOMPULSEの具体的な特徴(ウォレットバイトXOR、マルチチェーンBlockscout)は1対1のフィンガープリントではないが、この技術クラスは現在、北朝鮮のものとして分類されている。
  • デスクトップ暗号通貨ウォレット列挙セットledgertrezorbitcoin-coreelectrumexodusatomicguarda )は、北朝鮮が関与しているとされるUnit 42のmacOS向けRustDoor / Koi Stealerのターゲットリストと非常によく一致しています。
  • 同じ被害者プロファイルに対するクロスプラットフォームのWindows + macOSインプラント(以前のREF6598投稿では、C2が0x666[.]info 、Telegramフォールバックがt[.]me/ax03botにあるmacOSの兄弟について説明しました)は、BlueNoroffの特徴です。
  • Arctic WolfのBlueNoroffに関する報道 によると、 Telegramやメッセンジャーをターゲットにすることは 、BlueNoroffの得意分野であるとのことです。

リゾルバウォレットの既知平文署名を使用して新しいC2ドメインを探索する

ブロックチェーンリゾルバで使用されるXOR方式は、防御側が単一のウォレットだけでなくチェーン全体に対して攻撃できる、安定した2バイトの署名を漏洩させる。

2つの事実が組み合わさります。すべてのC2 URLはhthttp://またはhttps://から)で始まり、XORキーはウォレットのASCIIアドレスをそのまま表しているため、最初の2バイトのキーは常に文字0xになります。ht0x XOR演算を行うと\x58 \x0cが得られます。PHANTOMPULSE スタイルのリゾルバによって生成された暗号化されたinputフィールドは、どのチェーンでも、どの関連ウォレットによって署名されていても、4 つの 16 進数文字580cで始まります。

これにより、追跡対象が1つのウォレットを監視することから、署名を探すためにチェーン全体をスキャンする作業へと変化する。公開されているイーサリアム、ベース、オプティミズムのトランザクションデータは、BigQuery、Dune、またはフルアーカイブノードを介して照会可能です。公開されているイーサリアムトランザクションデータセットに対して、 0x580cで始まるinput値を検索し、最近のブロックタイムスタンプのウィンドウに範囲を絞ると、同じコードベースで使用されているこれまで知られていなかったリゾルバウォレットが明らかになります。各一致は、送信者ウォレットのASCIIアドレスをキーとしてデコードすることで検証されます。実際のC2 URLは、デコード後にhttpで始まります。以下のCyberChefレシピを使用すると、C2 URLを復号化できます。

SELECT 
    block_timestamp AS block_time, 
    from_address AS `from`, 
    to_address AS `to`, 
    input AS data
FROM `bigquery-public-data.crypto_ethereum.transactions`
WHERE block_timestamp >= '2026-04-01 00:00:00'
  AND input LIKE '0x580c%'
ORDER BY block_timestamp DESC
LIMIT 10000;

CyberChefは、以下のスクリーンショットに示すように、入力データを復号化してドメインを明らかにすることができます。

まとめ

PHANTOMPULSEは、公開されているコンポーネント(モジュール・ストンピング、デバッグAPIステートマシン、手動マッピング、ハードウェアブレークポイントAMSI/WLDP/ETWバイパス、スケジュールタスクの永続化、ブロックチェーンC2)から設計されています。その組み合わせと、それらを結びつける強化策は、活発に開発が進められている成熟したコードベースを示している。永続的なシグナルは動作に関するものであり、 REF6598 の Elastic の動作保護によってカバーされます。

PHANTOMPULSEとMITRE ATT&CK

Elasticは、MITRE ATT&CKフレームワークを使用して、企業ネットワークに対してAdvanced Persistent Threatが使用する一般的な戦術、手法、手順を文書化しています。

戦術(Tactics)

戦術とは、技術やサブ技術の「なぜ」を表すものである。それは敵の戦術目標、つまり行動を起こす理由である。

手法

手法は、敵対者がアクションを実行することによって戦術的な目標を達成する方法を表します。

修復

ヤラ

Elasticセキュリティは、このアクティビティを識別するためのYARAルールを作成しました。

観測

すぐれた監視性タイプ名前参考
33dacf9f854f636216e5062ca252df8e5bed652efd78b86512f5b868b11ee70fSHA-256のファントムパルスラットFinal payload
70bbb38b70fd836d66e8166ec27be9aa8535b3876596fc80c45e3de4ce327980SHA-256のsyncobs.exeファントムプルローダー
def66275fa3baffb16e6e4ae0297861d9790ae7161fbc271a2ba05d121f13c70SHA-256のビーコンを発進させようGTESTIC_WIN チェックイン
panel.fefea22134[.]netドメインC2パネルPHANTOMPULSE ハードコードされたフォールバック
fea22134[.]netドメインC2 domainバイナリで暗号化されています
195.3.222[.]251IPv4-アドレスステージングサーバーPowerShell/ローダー配信
0xc117688c530b660e15085bF3A2B664117d8672aA暗号通貨ウォレットブロックチェーンC2ウォレットETH/ベース/楽観主義
0x38796B8479fDAE0A72e5E7e326c87a637D0Cbc0E暗号通貨ウォレット資金ウォレットC2決議資金調達
eth.blockscout[.]comドメインブロックチェーンプロバイダーC2 URL解決
base.blockscout[.]comドメインブロックチェーンプロバイダーC2 URL解決
optimism.blockscout[.]comドメインブロックチェーンプロバイダーC2 URL解決
hVNBUORXNiFLhYYhmutex単一インスタンスXOR復号化
svcagent.dllファイル名スタブDLL永続性ペイロード
AssetMonディレクトリスタブDLLディレクトリ%ProgramData% または %APPDATA%
healthmon.exeファイル名ドロッパー元の実行ファイル名
diagcore.dllファイル名従来のサイドロードDLLMigrateSideloadによって移行されました
.elevateファイル名標高標識高架式再始動ルート
DotNetSvcUpdateTaskスケジュールされたタスク一次持続性3分間隔
DotNetSvcCoreTaskスケジュールされたタスクシステム永続性15分、隠れた
DotNetSvcUserTaskスケジュールされたタスクユーザー永続性ログオントリガー
EdgeWebViewUpdateTaskスケジュールされたタスクレガシータスクアンインストール中にクリーンアップされました
\Microsoft\Windows\NetFramework\DotNetSvcCoreTaskタスクURIブートタスクパス非表示のスケジュール済みタスク
Elevation:Administrator!new:{A6BFEA43-501F-456F-A845-983D3AD7B8F0}コムモニカーUACバイパス昇格された ITaskService
0x666[.]infoドメインmacOS C2macOSドロッパー
t[.]me/ax03botURLTelegramのフォールバックmacOS C2 デッドドロップ
thoroughly-publisher-troy-clara[.]trycloudflare[.]comドメイン以前のC2Cloudflare Tunnel

参照資料

本分析で参照されている過去の報告書およびツールキット:

この記事を共有する