- 本文はフォーティネット セキュリティ ブログの抄訳です。
執筆者:Zhongchun Huo
2014年1月29日
Neurevt(別名Beta Bot)は2013年3月頃に闇市場に登場したHTTPボットであり、価格も比較的安価となっています。まだ試験段階のものですが、このボットは拡張可能かつ柔軟性の高いインフラストラクチャーを持ち、すでに多くの機能を備えています。インストール時にはほぼすべてのユーザープロセスに自身を注入し、システム全体を乗っ取ります。さらに、注入したコードの連携にWindowsのメッセージとレジストリを利用するというメカニズムも利用します。このボットはHTTPリクエストを介してC&Cサーバーと通信を行います。通信データのさまざまな個所が個別に暗号化(大抵はRC4で)されています。今回の記事では、このボットのインフラストラクチャー、通信プロトコル、暗号化スキームを詳細に見ていきましょう。(今回の解析は私たちが2013年3月から6月までに収集したサンプルに基づいて行われています)
インストール/配備
インストール プロセス
大抵のマルウェアと同様、Neurevtはシステムフォルダに自身をコピーすることからインストールを始めます。フォルダはWindowsのバージョン、インストールされているサービスパック、OSが64ビットかどうかなど、マシンの特性に応じてフォルダが選択されます。
例えばWindows XP SP2を実行しているx86のマシンでは、%PROGRAM FILES%\ COMMON FILESフォルダが選択されます。インストーラーは「winlogon.{2227A280-3AEA-1069-A2DE- 08002B30309D}」という名前のサブフォルダを作成します。
このフォルダ名の最初の部分にある「winlogon」はボットのコンフィギュレーションから入手したものであり、後半部分はフォルダをWindowsエクスプローラの「Printers and Faxes」にリンクさせるための特殊GUIDです。このフォルダは、マルウェアがリスタートするたびに起動ポイントの機能を果たします。そしてインストーラーはこの新ファイルを起動させ、自身は終了します。
新たに起動されたコピーがシステムアプリケーションのプロセスを作成し(これも条件により異なる)、注入を始めます。注入されたデータはメモリの連続したブロック内にあり、データレイアウトは次のようになっています。
マルウェアが別のプロセスに何かを注入するのはこれが初めてになるため、注入されたコンテンツは単独のアプリケーションとして実行されます。これをプライマリインスタンスと呼び、別のプロセスに注入された他のインスタントと区別することとします。
プライマリインスタンスはすべての実行されているプロセスを検索し、次の条件を満たすものに注入を行います。
ここでは、注入されたデータはプライマリインスタンスと同じレイアウトになっています。唯一の違いは「local cfg」です。一部のコンポーネントはプライマリインスタンスのみでロードされるものであるため、データフィールドの一部が異なる行動をするように修正されています。
注入後は実行中のすべてのユーザープロセスに、マルウェアのインスタンスが1つあることになります。これらを「アシスタントインスタンス」と呼ぶこととします。すべてのアシスタントインスタンスに、プライマリインスタンスのステータスを監視するコードがあります。何らかの理由でプライマリインスタンスが終了すると、アシスタントインスタンスが起動ポイントからマルウェアのリスタートを試みます。
マルウェアが配備を終了すると、プライマリインスタンスはC&Cサーバーと通信を開始します。従来のウイルスがファイルシステムを感染させているプロセスのように思えますが、このマルウェアはファイルを感染させるのではなく、実行されているプロセスを感染させます。
ローカル情報の収集
1つ目のフラグにはWindowsのバージョン、インストールされているサービスパック、OSが32ビットか64ビットか、に関する情報が含まれています。2つ目のフラグには次のソフトウェア、あるいはベンダーに関する情報が含まれています。Net Framework、Java、Steam、SysInternalのツール、mIRC、Hex-Rays、Immunity Inc.、CodeBlocks、7-Zip、PrestoSoft、Nmap、Perl、Visual StudioおよびWireshark
また、以下に関する情報も含まれています。
- 1) システムにバッテリーがあるかどうか
- 2) システムにRDPの記録があるかどうか
- 3) システムでUACが有効になっているかどうか
4つ目のフラグにはインストールされているアンチウイルス ソフトウェアに関する情報が含まれています。Symantec、AVP、AVG、Avira、ESET、McAfee、Trend Micro、Avast、Microsoft Security Client、Bitdefender、BullGuard、Rising、Arcabit、Webroot、Emsisoft、F-Secure、Panda、PC Tools Internet SecurityおよびG Data AntiVirus
コンポーネント スレッド
このマルウェアはC&Cサーバーとの通信、データ整合性のチェック、スレッド(コンポーネント)間で受け渡しが行われるメッセージの管理、USBドライブの監視と感染など、さまざまな種類のジョブを実行するためのスレッドを作成します。これらのスレッドはソフトウェア コンポーネントのようなものです。スレッドを適切にロードするため、マルウェアはこれらを作成するための関数を定義します。この関数の定義は以下のようになっています。
ThreadProcは特定のジョブを実行するルーチンであり、idxはマルウェアがそれに割り当てたインデックスです。0-0x1Eと0x21はこれらの独自のルーチンに与えられたidx値です。これらのルーチンに対して実行されているスレッドは1つとなります。0x1Fと0x20は複数のインスタンスルーチンに使われます。NewComponentThreadがこれらの2つの値にセットされたidxで呼び出されると、この関数は新たなidxをThreadProcに割り当てます。これが0x22の1つ目の使用可能な(割り当てられていない)値です。
このマルウェアはこの関数によって作り出されたすべてのスレッドを追跡するためのリストを保持しています。各ThreadProcはそのidxにより指し示されるエントリを取り込みます。リストのエントリは次のような構造になっています。
新たな「コンポーネント スレッド」を実際に開始する前に、NewComponentThreadがThreadProcにショートコードスタブとラッパー関数を加えます。
このコードスタブはMZヘッダー内のランダムな場所でntdllイメージに書き込まれます。このランダムな場所が、NewComponentThreadがCreateThreadを呼び出す際のStartAddressになります。つまり「コンポーネント スレッド」の開始アドレスはntdllイメージのメモリ範囲内にあるということです。この機能はマルウェアがスレッド間でメッセージの受け渡しを行う際に使用されます。
ラッパー関数はデバッガーからスレッドを隠すことを試み、ThreadProcの呼び出しの前後にスレッドリストを更新します。また、特定値1234を使用して特定のTLSスロットを設定します。このマルウェアのコードは常に注入されたプロセス内で実行されているため、ホストプロセスの動きを監視し、操作するためにAPIフックが用いられることとなります。この特定のTLSスロット値はAPIフックを適用すべきでないマルウェア自身のスレッドの識別に使用されています。
APIフック
このマルウェアは2つの方法でRing 3フックを適用します。まず、以下のZwから始まるAPIに対し、事前演算フィルターを追加します。
ZwCreateFile
ZwOpenFile
ZwDeleteFile
ZwSetInformationFile
ZwQueryDirectoryFile
ZwCreateKey
ZwOpenKey
ZwSetValueKey
ZwOpenProcess
ZwTerminateProcess
ZwCreateThread
ZwCreateThreadEx
ZwResumeThread
ZwSuspendThread
ZwSetContextThread
ZwOpenThread
ZwUnmapViewOfSection
ZwDeviceIoControlFile
ZwQueueApcThread
このフィルターはまず指定のTLSスロットのチェックを行います。その値が1234であれば、それは呼び出しスレッドがマルウェアのものであるという意味です。フィルターは何もせず、スレッドに本物のAPIを呼び出させます。TLSスロットが1234でなければ、フィルターは演算が行われることになるオブジェクト(プロセス、スレッド、ファイル、レジストリ)を調べ、オブジェクトがマルウェアのものである場合には呼び出しスレッドにエラーステータスを返送します。
Ring 3フックを適用する2つ目の方法は、次の2つのグループのAPIにインラインフックを適用する方法です。
- 1) getaddrinfo、GetAddrInfoW、DnsQuery_W
- 2) HttpSendRequestW、PR_Write
このマルウェアは望ましくないホストをブロックするためにグループ1のAPIをフックします。ホストリストはボットサーバーから受け取ります。望ましくないホストのほとんどがアンチウイルスソフトウェア ベンダーのウェブサーバーです。
もし注入されたプロセスが次のブラウザプロセスの1つである場合、このマルウェアはグループ2のAPIをフックします。
- firefox.exe
- iexplore.exe
- chrome.
このマルウェアはボットサーバーから監視することとなるURLのリストを受け取ります。ブラウザがこれらのURLにリクエストを送信すると、このマルウェアはリクエストデータを捕え、ボットサーバーに送り返します。
メッセージの処理
システムではマルウェアのインスタンスが複数実行されています。これらのインスタンスを連携させるため、このマルウェアはアプリケーション定義のメッセージのハンドラーとして各インスタンスにスレッドを作成します。メッセージのほとんどはプライマリインスタンスがボットサーバーから何らかのものを受け取った後、プライマリインスタンスから送信されます。上に挙げたブロックされているホストリスト、監視されているURLのリストなどのローカルデータを更新するよう他のインスタンスに伝えるためです。
マルウェアのスレッドがメッセージを送信する場合、システムで実行されているすべてのスレッドを列挙し、ntdllイメージ内に開始アドレスを持つものを探します。「Component thread」の部分で述べたように、NewComponentThreadによって作成されるすべてのスレッドがこの条件を満たします。送信スレッドはPostThreadMessageを呼び出し、これらに対してメッセージを送信します。
これらのスレッドのなかで、(すべてのマルウェアインスタンスの)メッセージハンドラーのみがどのウィンドウ(PostThreadMessageにより送信されるメッセージの機能)とも関連のないメッセージに対する待ち行列を持っています(IsGuiThreadを呼び出すことで)。つまり、これらのハンドラーがメッセージとレスポンスの回収を行います。
メッセージが送信される前に、送信スレッドはメッセージ識別子に事前定義された修飾子を加えます。この修飾子はボットコンフィギュレーションの署名文字列とコンピュータ名に基づいて計算されます。その値は0から31のいずれかになります。wParamとlParamも、プロセス識別子やスレッド識別子のような特定の意味を持つ値を持っていることが多いため、これらも修飾されます。
ハンドラースレッドで、これらの値はメッセージが処理される前にリバース計算によって元の状態に戻されます。つまり、マルウェアのスレッド間で受け渡しが行われるメッセージを監視していたとしても、その内容を理解するのは困難だということです。ハンドラースレッドがサポートしているメッセージを表1に挙げました。
レジストリのデータの共有
このマルウェアはレジストリ値のすべてのインスタンスの共有データを格納しています。これらの値はHKCU\ Software\CLSID{ランダムなGUID}{コンフィギュレーション署名文字列のハッシュ}という鍵のもとで作成されます。
以下はこのマルウェアが使用する重要な値です。
CS1\S02: 最後に受け取ったブロックホストリストを格納する暗号化データ CS1\S03: 最後に受け取った監視URLリストを格納する暗号化データ CS1\S01: 最後に受け取ったコンフィギュレーション CG1\CF01: 最後に受け取ったレスポンスのオフセット0x20にあるDWORDの値 CG1\CF02:最後に受け取ったレスポンスのオフセット0x24にあるDWORDの値 CG1\CF03: 最後に受け取ったレスポンスのオフセット0x28にあるDWORDの値 CG1\BIS: リムーバブルディスクでプロセスが実行されていることを示すフラグ CG1\BID: 最初の起動時間 CG1\HAL: DWORD、インストールが成功した後に0xEE05にセットされる CG1\LCT: 最後に受け取ったボットレスポンスの時間
ボットの通信
NeurevtはHTTPを介してC&Cサーバーと通信を行います。リクエストとレスポンスの両方がRC4アルゴリズムで暗号化されています。感染させたマシン上でマルウェアがボットサーバーに最初のリクエストを送信すると、通信が始まります。その後、通信は「Q&A」方式で行われます。
ボットコンフィギュレーション
Neurevtにはコンフィギュレーションが組み込まれています。コンフィギュレーションデータと復号コードはどちらもRC4で暗号化されています。このマルウェアはヒープ上にメモリブロックを割り当て、コンフィギュレーションを復号します。
スレッドに渡される引数は次の構造へのポインターです。
DecryptFuncとGetCriticalSectionの間のフィールドは復号機能が用いる関数です。暗号スレッドはまず、鍵シーケンスのハッシュ値を計算し、PEイメージに埋め込まれている値と比較することでインテグリティチェックを実行します。ハッシュ値が一致している場合、スレッドはメモリブロックを割り当て、コンフィギュレーションデータをメモリ内に復号します。
lpKeyForReEncryptは再暗号化を行うための4バイト鍵シーケンスへのポインターです。これはコンフィギュレーションデータが復号された後にランダムに生成されます。再暗号化にもRC4アルゴリズムが用いられます。コンフィギュレーションデータをメモリダンプから発見されないように保護するためです。再暗号化は復号スレッドが終了した後にメインスレッドによって行われます。復号スレッドは終了する前に、lpKeyForReEncryptによって与えられたメモリブロックにメモリポインターと再暗号化鍵を格納します。
その後のコンフィギュレーションデータへのアクセスはすべて次の手順で行われます。
- 1) メモリブロックを割り当て、再暗号化したデータをその中へコピーする。
- 2) 復号を行い、必要なデータを取得。
- 3) データを再暗号化する。
- 4) メモリを開放する。
コンフィギュレーションデータには718バイトのヘッダーがあり、これには表2に示すフィールドが含まれています。
コンフィギュレーションデータの0x2ceに配列があります。各エントリにC&Cサーバーに関する情報が格納されています。通常、1つのコンフィギュレーションに3つ以上のエントリがあります。エントリのサイズは0x280バイトです。表3に重要なフィールドを挙げました。
リクエストの形式
各リクエストには128バイトのデータブロックが含まれ、その中に表4に示すフィールドが含まれています。
このマルウェアはRC4でデータブロックの暗号化を行います。2つの要素で構成される12バイトの鍵シーケンスを用います。最初の要素はコンフィギュレーションデータのoffset 0x26eにある選ばれたエントリから取得した8バイトのシーケンスです。2つ目の要素はCryptGenRandomを呼び出すことで取得したランダムなシーケンスです。シーケンス長は8から27バイトです。鍵のこの部分と暗号化されたデータブロックがクエリ文字列のなかに挿入されます。
これがボットサーバーへ送信される最初のリクエストであれば、次の3つのCSフィールドも含まれることになります。
- 1) インストールされたマルウェアのフル ファイルパス
- 2) NameSamCompatible形式のユーザー名
- 3) デフォルトのウェブブラウザの名前
これらの文字列はloop-XORアルゴリズムで個別に暗号化され、URLクエリ文字列に連結されます。
ボットレスポンスのヘッダーの形式
ボットレスポンスには0x5cバイトのヘッダーと最大8ストリーム(実際に使用されるのは4ストリームのみ)で構成される本文が含まれています。ヘッダーも本文もどちらもRC4で暗号化されており、個別に復号が行われます。ヘッダーの最初の4バイトはコンフィギュレーションの8バイトのシーケンスに追加され、ヘッダーを復号するための12バイトの鍵シーケンスとなります。ヘッダー内の次の4バイトとコンフィギュレーションの別の8バイトのシーケンスが連結され、レスポンス本文のための別の12バイトが構成されます。
表5はレスポンスのヘッダーの構造です。
レスポンスのヘッダーのControlフラグは、セキュリティソフトウェアを無効化するルーチンや偽のポップアップ警告ウィンドウを起動するなど、マルウェアのさまざまな行動をトリガーするビットフラグです。マルウェアがUACを迂回できるようユーザーをだまして許可を得るためのものです。
0x20から0x28のフィールドはレジストリ値に格納される3つのDWORDです。これらはマルウェアが次に送信するリクエストでボットサーバーへコピーされます。
0x3Cにある長配列には8つのエントリがあります。これらはレスポンス本文のストリームの長さです。
ボットレスポンスの本文
レスポンスのヘッダーの形式に従えば、本文には8つのストリームがあるはずですが、このマルウェアには最初の4つのストリームに対する処理ルーチンしかありません。
最初のストリームにはボットサーバーから送り返されたボットコマンドが含まれています。このストリームの最初の文字はストリーム内のコマンドの総数です。各コマンドはナル終端された文字列(前にデータブロックが付く)として格納されています。データブロックのサイズは22バイトです。これはマルウェアのコードのどの部分でも使われていません。
マルウェアはハッシュ値によりコマンドキーワードを識別します。処理ルーチンのリストがあり、リストの各エントリは次のような構造になっています。
2つ目のストリームにはブロックされるホストのリストが含まれています。このリストは次のフック済みの関数で用いられます。
- DnsQuery_W
- GetAddrInfoW
- getaddrinfo
3つ目のストリームにはURLのリストが含まれており、マルウェアはこれに対して送信されたHTTPリクエストの監視を行います。このリストは次のフック済みAPIで使用されます。
- HttpSendRequestW
- PR_Write
レスポンス本文の4つ目のストリームには、新たなコンフィギュレーションの生成に使用することができるデータが含まれています。このストリームはINIファイルの形式と似た形式になっています。このマルウェアはこのストリームを、コンフィギュレーションの部分で述べた構造に編成されているバイナリのデータブロックにコンパイルします。
Skypeを介したスパム
このマルウェアはSkypeを用いて、ボットサーバーから受け取るテキスト素材を拡散します。拡散ジョブを起動させるボットコマンドは2つあります。
ボットサーバーはテキストファイルを指し示すURLパラメータとともにコマンドを送信します。テキストファイルの各行にはロケール(セミコロンで区切られたメッセージのペア)が含まれています。
{locale name};{spam content}
このマルウェアはシステムのデフォルト言語のロケールに従って1つの行を選択し、その行のメッセージをSkypeのエコーサービスの名前である「echo123」以外のすべての連絡先に送信します。
メッセージを送信するため、マルウェアはコマンドラインのパラメータを「/ssp {URL sent by bot server}」に設定し、自身の新たなプロセスを作成します。この新たなプロセスがSkypeのAPIを使い、自身とSkypeクライアントの間の通信を設定します。その後、Skypeコマンドの送信を開始します。
送信される最初のコマンドは「SEARCH FRIENDS」であり、これがログインしているユーザーのすべての連絡先を回収します。各連絡先に対し、「MESSAGE」コマンドがSkypeクライアントに送信され、選択されたスパムコンテンツを送信するためのIMメッセージが生成されます。
UACの迂回
UACが有効になっているシステムでは、特権を昇格させる必要があっても、このマルウェアはユーザーへのプロンプトを妨害したり、UACを無効化してしまうようなトリックは使いません。その代わり、直接ユーザーに許可を「求め」ます。
このマルウェアは「Cmd.exe」のプロセスを作成し、マルウェアのファイルパスをコマンドラインの引数に置きます。プロンプトウィンドウがポップアップするということはつまり、Windowsのアプリケーションである「Cmd.exe」が特権昇格を求めていることになります。不注意なユーザーはこのリクエストに許可を与えてしまうでしょう。その後、マルウェアの新たなプロセスが「Cmd.exe」により作成され、「Cmd.exe」のシステム特権を引き継ぎます。
「show detail」をクリックすると、詳細が表示される(図18)
場合によっては、高い特権を得るために、システム破損の偽警告を出し、ユーザーに「復旧ユーティリティ」の許可を求めます(図19)。
ユーザーがこれを拒否すると、マルウェアは警告のポップアップを続け、数度にわたってユーザーへのプロンプトを行います。
このマルウェアは次の言語で警告を行うことが可能です。ロシア語、ポルトガル語、ドイツ語、フランス語、オランダ語、アラビア語、ヒンディー語、ペルシャ語、簡体字中国語、トルコ語、インドネシア語、イタリア語、スペイン語、ポーランド語、日本語、ベトナム語
システムのデフォルトの言語ロケールに基づいてこれらの1つを選択し、ユーザーに疑念を抱かせるような言語の不一致を避けます。
結論
今回の解析が示すように、この通信プロトコルには将来的に新たな機能が追加できるよう十分なスペースが確保されています。リクエストとレスポンスの構造には未使用のフィールドがあり、レスポンスデータには処理すべきコードを含まないストリームが4つあります。Neurevtは市場に登場したばかりですが、このボットの作成者はおそらく、急速に開発を進めていくでしょう。近い将来、Neurevtの新たな機能が登場するのは間違いないと思われます。