DNS通信に悪意あるデータを隠す「TrickBot」亜種 Windows端末を狙う新たな攻撃手法

文●フォーティネットジャパン 編集●ASCII

提供: フォーティネットジャパン

  • この記事をはてなブックマークに追加
  • 本文印刷

本記事はフォーティネットジャパンが提供する「FORTINETブログ」に掲載された「DNSトンネリングを悪用するTrickBot亜種の内部を探る」を再編集したものです。

米国時間2026年7月22日に掲載されたフォーティネットブログの抄訳です。

影響を受けるプラットフォーム:  Microsoft Windows
影響を受けるユーザー:      Windowsユーザー
影響:              被害者のコンピュータを完全に制御
深刻度:             高

背景

 FortiGuard Labsは新たに、不正な形式のDNSクエリを送信する複数の悪意のあるサンプルを確認しました。詳細な解析を行った結果、これらのサンプルは、DNSトンネリングを利用してコマンド&コントロール(C2)サーバーと通信するTrickBotの亜種であることが判明しました。

 TrickBotは、FortiGuard Labsが過去10年にわたり繰り返し観測してきたモジュール型マルウェアファミリーです。モジュール型アーキテクチャを採用しているため、侵害したデバイスに追加のモジュールをダウンロードして実行することで、機能を拡張できます。これまでに確認されていたTrickBotの亜種は、主にHTTPを使用してC2サーバーと通信していました。

 DNSをベースとするTrickBotの亜種としては、Anchor DNSをはじめ、これまでにも公開された事例がありますが、最近確認されたサンプルに焦点を当て、その実装と振る舞いを技術的に詳しく解析します。

 今回、これらのサンプルの1つを取り上げ、このTrickBot亜種が解析を回避するために用いる難読化手法を解説します。また、侵害したデバイスで確立する永続化の仕組みや、C2通信のために不正な形式のDNSクエリを生成するプロセス、さらにDNSパケットを介してコマンド&コントロールデータをやり取りする手法についても解析します。

静的解析

 図1は、取得したTrickBotのサンプルファイルを示しています。このうち1つのサンプルをPE解析ツールで調査したところ、64ビット実行ファイルであり、パッカーによって保護されていないことが確認されました。

図1:取得されたサンプルとPEツールでの解析

 これらのサンプルのファイルハッシュは異なるものの、PEの特性、開始コード、および全体的な動作は共通しています。このことから、同一のTrickBotキャンペーンに由来するものと考えられます。

難読化手法

 静的な解析を回避するため、TrickBotはいくつかの難読化手法を採用しています。

 このTrickBot亜種では、定数文字列は暗号化されて保持され、実行時に復号されます。また、ほとんどの定数値は実行時に動的に計算されます。図2は、このマルウェアが「Kernel32.dll」という定数文字列を生成して復号する処理を示しています。この復号方法では、SUB命令またはXOR命令が実行されます。

図2:定数文字列「Kernel32.dll」の復号

 すべてのWindows APIは、ハッシュベースのルックアップによって実行時に解決されます。この処理を行う関数を、ここではGet_API_By_Hash()と命名しています。図3は、このマルウェアがハッシュ値C8AC8026をアドレス0x14004990の関数に渡し、その関数がLoadLibraryA()のAPIアドレをRAXレジスタに返す仕組みを示しています。TrickBotはこのAPIを呼び出して、「Kernel32.dll」ファイルを読み込みます。このファイル名は、先に復号された定数文字列です。

図3:LoadLibraryA() APIの動的な取得

 このTrickBot亜種で使用されている一部のAPIのリストと、それぞれに対応するハッシュコードを付録に掲載しています。

永続化の継続

 このTrickBotは、Windowsタスクスケジューラーを悪用して被害者のコンピュータで永続化を維持します。

 このマルウェア亜種は、「-u」、「-s」、「--log」、「-i」などのコマンドラインパラメータを受け付けます。

 TrickBotの実行ファイルをパラメータなしで起動した場合、または「-u」フラグを指定せずに起動した場合は、永続化を維持するためのワークフローが実行され、数分ごとに実行される新しいタスクが作成されます。

 タスク名は、%AppData%フォルダ内からランダムに選択したフォルダ名、復号した定数文字列「autoupdate #」、およびランダムな数値を組み合わせて生成されます。図4に示すように、このマルウェアはタスク名を生成する関数を呼び出し、「Wireshark autoupdate #72784」のような名前を生成します。このようにTrickBotは、スケジュールタスクをWiresharkの自動更新であるかのように偽装します。

図4:ランダムなタスク名を生成するために関数を呼び出す

 スケジュールタスクを作成するために、このマルウェアはタスクスケジューラーの自動化インタフェースを実装するITaskService COMオブジェクトのインスタンスを生成します。COMオブジェクトを生成するために、ProgID「Schedule.Service.1」を指定してCoCreateInstance() APIが呼び出されます。

 続いて、COMオブジェクトが提供する複数のインタフェースを使用して、タスク名、タスクのアクション、タスクトリガーなどのタスクのプロパティを設定します。

 図5は、以下の設定でスケジュールタスクが正常にインストールされたことを示しています。

・タスク名:「Wireshark autoupdate #72784」
・タスクのアクション:パラメータ「-u」を付けてコマンド「E:\4d99f6b.exe」を実行
・トリガー:デバイスの起動時に実行し、5分毎にこのタスクアクションを繰り返す

図5:タスクスケジューラーで作成されたタスク

 TrickBotは、タスク名と実行ファイルのフルパスを、2つのNTFS代替データストリーム(ADS)に保存します。「$TASK」という名前のストリームにはBase64でエンコードされたタスク名が、「$FILE」という名前のストリームにはBase64でエンコードされたTrickBot実行ファイルのフルパスが格納されています。

 実行時には、まずこれら2つのストリームからデータを読み取り、その後、取得した情報を使用して同じコードフローを実行し、スケジュールタスクを再作成します。これにより、TrickBotは異なるタスク名で重複したスケジュールタスクが作成されることを防いでいます。

図6は、「$TASK」および「$FILE」ストリームに保存されたBase64でエンコードされたデータを検証およびデコードするために使用した3つのコマンドの実行結果を示しています。

図6:ADSストリームと抽出されたデータ

DNSトンネリング

 DNSトンネリングは、標準的なDNS(Domain Name System:ドメインネームシステム)のクエリおよび応答にDNS以外の通信を隠ぺいすることで、ネットワークセキュリティ対策を回避する手法です。図7は、DNSトンネリングの一般的な仕組みを示しています。

図7:DNSトンネリングの仕組み

 TrickBot実行ファイルを「-u」パラメータを付けて起動すると、DNSトンネリングを介してC2サーバーと通信します。図8は、TrickBotがパブリックDNSサーバーである「8.8.8.8」に対して、不正な形式のDNSクエリを送信している様子を示しています。

図8:TrickBotの亜種とパブリックDNSサーバー間のDNSトラフィック

TrickBotのDNSトンネリング:DNSクエリ

 このマルウェアを解析した時には、元のC2サーバーはすでに停止していました。そのため、解析を継続できるよう、Pythonスクリプトを作成してC2サーバーの応答をシミュレートしました。この解析で示しているデータは、シミュレーションしたC2サーバーから取得したものです。

 TrickBotは、コマンド&コントロール(C2)のリクエストパケットを暗号化されたDNSクエリパケットに格納して送信します。このセクションでは、TrickBotが3種類のパケットを使用してデータを転送する仕組みを詳しく説明します。

データのエンコードとドメイン名の構築

 マルウェアは、制御コマンドのデータを暗号化してフォーマットした後、DNSトラフィックに埋め込みます。この処理は、次の手順で行われます。

1.暗号化:コマンドデータをキー0xB9によってXOR演算します。
2.16進数エンコード:暗号化したデータを16進文字列に変換します。
3.サブドメインのフォーマット:正規のドメイン構造を装うため、16進文字列の63文字ごとにピリオド(.)を挿入します。
4.ドメイン名の付加:フォーマットした文字列の先頭に、復号した定数ドメイン「.westurn.in」を付加します。

8970D9E4F4BE8F88FD3394A2C57A1763E2B9BA96D8D7DAD1D6CBE6DDD7CA96F.DFCEAF2EDF6E994FC8 1F5FF81F3EDE6EE8F8B808B898997FF8EFF8BFF8D818B.FF8AFBFA8F8A8D8CF88D8DFC8DF88FFD8F8D8E 8E8A88F8FC968996EED0D7DDD.6CECA998199C18F8D968889898896899789978997.westurn.in

 TrickBotは、Windows APIの getaddrinfo()関数を利用して、不正な形式のDNSクエリを送信し、その応答を取得します。

パケット構造

 パケットの平文データを図9に示します。このTrickBot亜種が生成するすべてのDNSクエリパケットは、このパケット構造に従っています。

図9:0x30パケットの平文データ

 図9に示す平文パケットは、次のフィールドで構成されています。

パケットタイプ:0x30、0x31、0x32
パケットセッションID: 同一セッションに属するパケットを関連付けるために使用される、16バイト長のUUID
フラグメンテーションヘッダー:先頭の1バイトはフラグメントインデックスを示し、次の1バイトはフラグメントの総数を示します。この例では、フラグメントインデックスは0、フラグメントの総数は3です。

注:0x30タイプのパケットサイズが96バイト(0x60)を超える場合、TrickBotはパケットを複数の小さなパケット(各パケットの最大サイズは96バイト)に分割します。このフィールドは、0x30パケットにのみ存在します。

パケットペイロード:制御コマンドのペイロードデータ

 図10は、Pythonで作成したDNSサーバーのログを示しています。このログから、3つに分割された0x30パケットが受信され、再構築される様子が分かります。

図10:0x30フラグメントパケットが再構築される過程

パケットタイプの機能

 3種類のパケットは、運ぶデータの内容に応じて、それぞれが異なる役割を果たします。

・パケットタイプ 0x30(リクエスト):TrickBotの制御コマンドのリクエストデータをC2サーバーへ送信します。

・パケットタイプ 0x31(サイズクエリ):後続の応答パケットのサイズをリクエストします。サイズ情報を受信すると、TrickBotは受信データを格納するために必要なメモリバッファを確保します。その後、データは0x32タイプのパケットで送信されます。

・パケットタイプ0x32(応答データ):TrickBotの制御コマンドの応答データを転送します。TrickBotがこのタイプのパケットを送信する際、ペイロードには次の2つのDWORDが格納されています。

 ・1つ目のDWORD:前の0x30パケットに対する応答で受信した値。
 ・2つ目のDWORD:データオフセット。この値は、C2サーバーに対してデータ転送を開始する位置を指示します。

TrickBotのDNSトンネリング:DNS応答

 通常のDNS応答パケットには、クエリしたドメインに対応するIPアドレスが含まれます。このセクションでは、TrickBotがこれらの応答のIPアドレス内に悪意のあるデータを隠す仕組みについて説明します。

 0x32応答パケットは、C2サーバーがDNSトンネルを介してデータを転送する方法を示しています。これらのパケットは、TrickBotが先に0x30パケットで送信したリクエストに対応する制御コマンドの応答データを配信する役割を担います。

IPインデックスとデータのエンコード

 TrickBotは、各IPv4アドレスの第1オクテットの上位6ビットを、1から始まるインデックス値(IPインデックス)を予約し、下位2ビットは使用しません。そのため、図11に示すように、第1オクテットを2ビット右シフト(4で除算するのと同等)することで、IPインデックスを取得できます。

図11:0x32タイプの応答パケット

 最初の2つのIPv4アドレスが予約されており、図11に示す特定の役割が割り当てられています。

・1つ目のIPアドレス:データオフセット(例:0x9F2B8)
・2つ目のIPアドレス:現在のDNSパケットに含まれるペイロードデータのサイズ(例:183)

 6ビットのインデックスの最大値は0x3F(63)です。最初の2つのIPアドレスは予約されているため、ペイロードデータを格納できるのは残り61個のIPアドレスとなります。各IPアドレスには3バイトのペイロードを格納できるため、1つの0x32応答パケットで転送できるペイロードの最大サイズは、(63 - 2) × 3 = 183バイトとなります。

データの再構築

 DNSプロトコルでは、1つの応答パケットに複数のIPv4アドレスを含めることができます。しかし、中継DNSリゾルバでは、応答を返す前にこれらのIPアドレスの順序をランダムに並べ替える場合があり、その結果、エンコードされたペイロードの順序が入れ替わってしまうことがあります。この問題に対処するため、TrickBotは受信したIPアドレスをIPインデックス(第1オクテットの上位6ビット)に基づいて元の順序へ並べ替えるソート機能を実装しています。

 元のペイロードデータを再構築するため、TrickBotは3番目のIPアドレスから順に各IPアドレスに含まれる3バイトのペイロードを読み取り、あらかじめ確保したメモリバッファへ格納します。

 図12は、ソートしたIPアドレスから元のペイロードデータが再構築される仕組みを示しています。

図12:ソートされたIPアドレスから再構築されたデータ

性能分析

 私のテスト環境では、TrickBotのDNSトンネリングを介して1.2MBのファイルをTrickBot亜種へ転送するのに40秒を要しました。このときのDNSトンネリングによる転送速度は約30.7KB/秒でした。

TrickBotの制御コマンド

 次に、TrickBotの制御コマンドの構造と機能について詳しく説明します。

 DNSトンネル内で使用されるコマンドのデータ構造は、HTTPプロトコルを使用するTrickBotのバージョンと共通です。 以下では、制御コマンドを時系列に沿って順に見ていきます。

コマンド「0」のリクエスト

 C2サーバーへ最初に送信されるリクエストコマンドは、次の形式になっています。

/anchor_dns/DESKTOP-E8LF8JT_W629200.F7F2F482F3BC6345A44E4A6D647731AE/0/Windows 8 x64/1001/0.0.0.0/4E2D56B35DDF7C44B2E42B7B33AADD57EFC3411D262559337CE94D86BA6D76 F5/EjR6pa0ELV59E8K2mR8PUbI80DlEaxyb/

構造の内訳:

・「/anchor_dns」:キャンペーン名、または攻撃グループ名。
・「/DESKTOP-E8LF8JT_W629200.F7F……31AE」:被害者のClient_ID。コンピュータ名、OSバージョン、およびハードウェアIDで構成されています。
・「/0」:リクエストコマンド番号。
・「/Windows 8 x64/1001」:システム情報。なお、このマルウェアはWindows 10を誤って「Windows 8」と認識します。
・「/0.0.0.0」:本来は侵害されたデバイスのグローバルIPアドレスを表すためのものです。しかし、この値はマルウェアペイロード内で「0.0.0.0」にハードコードされています。
・「4E2D56......76F5」: ランダムに生成された64バイト(0x40)の16進データ。
・「EjR6pa0……axyb」:検証データ(セッションIDのような役割を果たす)。ランダムに生成される32バイト(0x20)の文字列で、コマンドセッションごとに一意であり、そのセッションでのみ有効です。

 コマンド「0」は、C2サーバーへ最初に送信されるコマンドで、被害者をC2サーバーに登録する役割を果たします。

 HTTPバージョンの動作を基に、DNSサーバー上でリクエストコマンド「0」に対する応答コマンドを作成し、シミュレートしました。

"/1/anchor_dns/DESKTOP-E8LF8JT_W629200.F7F2F482F3BC6345A44E4A6D647731AE/EjR6pa0ELV59E8K2mR8PUbI80DlEaxyb"

 TrickBotは、4つ目の「/」を直接特定して応答パケットを解析します。その直後に続く32バイトの検証データを、リクエストコマンドパケットで送信した検証データと比較します。

図13:TrickBotは、32バイトの検証データを確認する

 図13に示すように、TrickBotはmemcmpに似た関数を呼び出して検証コードを比較します。図の下部には、受信した応答パケットが表示されています。先頭の「/1/」は、この応答パケットにおいては他の有効なコマンド番号に置き換えることができます。TrickBotは、検証データより前にあるデータフィールド全体を無視するためです。

コマンド「1」のリクエスト

 TrickBotはC2サーバーへコマンド「1」を送信し、被害者のデバイス上で実行するタスクをリクエストします。コマンド「1」のリクエストパケットの例を以下に示します。

/anchor_dns/DESKTOP-E8LF8JT_W629200.F7F2F482F3BC6345A44E4A6D647731AE/1/ZQqRydar6QBCEYo4IMWUodsgQ7AzJhHc/

 これまでのコマンドと同様に、末尾の32バイト(0x20)の文字列は検証コードとして使用されます。TrickBotが応答を認証できるようにするため、C2サーバーはコマンド応答パケット内でこの値をそのまま返す必要があります。

 リクエストコマンド「1」に対する応答パケットの例を以下に示します。

/{response command number}/anchor_dns/DESKTOP-E8LF8JT_W629200.F7F2F482F3BC6345A44E4A6D647731AE/ ZQqRydar6QBCEYo4IMWUodsgQ7AzJhHc/96322307/bWFsaWNpb3VzX2V4ZV9tb2R1bGUgcGFyYW1ldGVy

 4つ目の「/」の後に続く32バイトの文字列は、前述した他のコマンドと同様に検証コードです。その後に続く「96322307」は、このパケット内のコマンド文字列を識別するための一意のコマンド文字列IDです。最後の部分(「bWFsaWNp…」)は、Base64でエンコードされたコマンド文字列です。

 Base64文字列をデコードすると、「malicious_exe_module parameter」というコマンド構造が得られます。このコマンドは、スペースを区切り文字として次の2つの要素に分けられます。

・モジュール名(必須): ダウンロードして実行するモジュールの名前(例:malicious_exe_module)。コマンド「5」などのリクエストコマンドに含まれます。

・パラメータ(オプション): モジュールの実行時に渡される引数(例:parameter)。
このTrickBot亜種は、以下の表に示す12種類の応答制御コマンドをサポートしています。

コマンド「5」のリクエスト

 TrickBotは、実行可能なモジュールをダウンロードするため、C2サーバーへリクエストコマンド「5」を送信します。この処理は通常、応答コマンド「1」、「3」、「4」、「5」および「7」のコマンドハンドラー関数によって開始されます。

 以下に、リクエストコマンド「5」のパケットの例を示します。

/anchor_dns/DESKTOP-E8LF8JT_W629200.F7F2F482F3BC6345A44E4A6D647731AE/5/test_dll/

・「5」:リクエストコマンド番号。
・「test_dll」:C2サーバーからダウンロードするモジュール名。この名前は、先に受信したコマンド「1」の応答パケット内でBase64エンコードされていました。

 応答パケットにはモジュールデータが含まれており、そのデータは複数の0x32タイプのパケットに分割されて配信されます。

図14:コマンド「5」をリクエストしてモジュールをダウンロードする処理

 図14は、応答コマンド「3」のハンドラ関数を示しています。この関数は、メモリ上に示されているDLLモジュールをダウンロードするため、コマンド「5」のリクエストパケットを送信します。TrickBotは、DLLモジュールをランダムな名前の一時ファイルとして保存します。

 その後、このマルウェアはCreateProcessW() APIを呼び出してrundll32.exeを起動し、ダウンロードしたDLLモジュールを実行します(図15参照)。悪意のあるDLLモジュール(tcp469A.dll)は、rundll32.exeプロセス内で実行され、その実行はエクスポート関数「Control_RunDLL」から開始されます。

図15:被害者のデバイスで、ダウンロードしたDLLモジュールを実行する処理

結論

 今回の解析では、従来の亜種で使用されていたHTTPプロトコルに代わり、コマンド&コントロール(C2)通信にDNSトンネリングを使用するTrickBot亜種について調査しました。DNSトンネリングは、DNSクエリおよびDNS応答に特殊なエンコード処理を施したデータを埋め込むことで実現されています。

 このマルウェアは、Windowsタスクスケジューラーを利用して永続化を確立し、NTFSの代替データストリーム(ADS)に設定情報を保存します。また、暗号化された文字列や実行時にAPIを解決することで、解析を困難にしています。

 モジュール型アーキテクチャにより、メモリ上でのモジュール実行が可能であり、コマンド実行、プロセスインジェクション、PowerShell実行などの機能をサポートしています。DNSトンネリングを採用していますが、このTrickBot亜種は、TrickBotファミリーを持続的な脅威にしてきたモジュール設計と機能を維持しています。

フォーティネットのソリューション

 フォーティネットのお客様は、FortiGuardのAntiSPAM、Webフィルタリング、IPS、およびアンチウイルスサービスにより、この攻撃キャンペーンからすでに保護されています。

 この悪意のあるキャンペーンで使用されているC2サーバーは、FortiGuard Webフィルタリングサービスにより、「悪意のあるWebサイト」として分類されています。

 このドメインは、FortiGuardのボットドメインDBサービスによっても検知されます。

FortiGateおよびFortiNDRは、次のIPSシグネチャにより、このDNSトンネリングを使用するTrickBotを検知します。

  Trick.Botnet

 FortiGuardアンチウイルスサービスは、これらのTrickBotサンプルファイルを次のアンチウイルス(AV)シグネチャで検知します。詳細については、「FortiNDR Cloud: DNS Based Threat Detection and Response」をご参照ください。

  W64/TrickBot.WC!tr

 FortiGate、FortiMail、FortiClient、およびFortiEDRは、FortiGuardアンチウイルスサービスをサポートしています。FortiGuardアンチウイルスエンジンは、これらすべてのソリューションに組み込まれています。これらの製品を最新のシグネチャを利用して運用しているお客様は、本レポートに記載されているマルウェアコンポーネントから保護されます。

 新たな脅威に関する最新情報を受け取るには、FortiGuard Labsのアラートサービスに登録してください。

 また、フォーティネットが無償で提供するNSEトレーニング:NSE 1 – 情報セキュリティ意識向上の実施もご検討ください。このトレーニングでは、インターネットの脅威に関する情報が含まれており、エンドユーザーは各種のフィッシング攻撃を識別して自らを保護できます。

 FortiPhishフィッシングシミュレーションサービスは、フォーティネットセキュリティ意識向上トレーニングサービスと連携し、実際のフィッシング攻撃シナリオを用いて、従業員に対して一般的なソーシャルエンジニアリング手法に対処できるようにトレーニングおよびテストします。これらのサービスは、ユーザーが不審なコンテンツを認識し適切に対応する能力を向上させることで、特に注意力が散漫になりやすい状況や緊急性を煽る攻撃に対する耐性を高め、フィッシング攻撃やマルウェア攻撃のリスクを低減できるように支援します。

 このようなサイバーセキュリティの脅威の影響を受けていることが疑われる場合は、グローバルFortiGuardインシデントレスポンスチームにお問い合わせください。

MITRE ATT&CKの手法

IOC(Indicators of Compromise:侵害指標)

C2サーバードメイン

westurn.in

関連するサンプルのSHA-256

[TrickBotのサンプル]

DF527A5C2FBDE43816CD02F4CD49EEE4BB82FB4A3C7045021360888C7D504C98
6C677EB2B3FFD288083C59A13D7BB712D4754AF61A5563873F76C440962346F4
105F652E6B8F31C371F2385877E43B6772AFF5D3168D5D4635F8A1FCBB321421
33C331EDEDBF8EE9829895424423CE3FD17E359D2E784FCBCE396AACFF458CF5
3B19A82E1354AC14A3DA7C840CBDD0CE50DB38432D78E767B36F08E45024C23D
BF80245BA792992FBFE24ABAC33F8FD66F24CDEB5F0F21CFDF45A29D107C8D3B

付録

■関連サイト

本記事はアフィリエイトプログラムによる収益を得ている場合があります