この投稿は、Emotetバンキング系トロイの木馬に関する連続ブログの続きです。「身代金」を要求するため感染が明示的なWannaCryとは異なる問題を生み出すEmotet。これまで、Emotetが仕込まれるメカニズムとその振舞いについて動的分析をしてきました。Emotetから得られたホストとネットワークに関するデータから、それ自身をサービスとして登録することで権限を昇格させ、ファイルシステムの様々な場所に存続し、最後にドロッパーファイルを削除することがわかっています。
今回の投稿では、メインのペイロードがどのように解凍され、その後実行を開始するのかをデバッガ上で説明します。Emotetの実行は、いくつもの段階の難読化されてメモリ内にロードされる実行プログラムで構成され、異なるメモリ領域全体に巧妙なコードフローを持たせます。これは解析者や静的解析エンジン、逆アセンブラーなどが、このトロイの木馬を解析するのを難しくするように設計されているのです。
また、解凍されたコードの中で、Emotetが特定のレジストリキーの存在をどれだけ早い段階でチェックするかについても記述します。そのレジストリキーにアクセスできなければ、ペイロードは解凍されず、実行もしません。キーに対する読み取りアクセスを遮断することが、Emotetペイロードの実行を妨げる効果的な方法であることがわかります。
Part 2では、Emotetがどのように他のマルウェア群のドロッパーとして働くのか、そして、これがどのように、攻撃者がサービスとしてのマルウェアのビジネスモデルを採り入れていることを示しているかを述べました。15.exeと呼ばれるEmotetの実行ファイルのひとつが、最終的にTrickbot という他のバンキング系トロイの木馬を拡散させたことが解析でわかりました。2019年4月(5月号)の「Bromium脅威情勢レポート」で特集したように、Emotetの数はあいかわらず膨大であり、Bomiumが隔離した中でもトップの脅威であることがわかっています。
Bromium(ブロミアム)は従来のマルウェア対策製品とは全く異なる発想で、外部からの脅威に対して防御・検知・分析します。
Bromiumをインストールしたコンピュータでは、Emotetは隔離されたマイクロ仮想マシンの中で実行し、システムの統合性を損なうことはありません。マルウェアは活発に活動するため、侵害の痕跡(IOC)を残すため、セキュリティチームはこれを使ってネットワークの他の資産を守ることができます。
パッカーの主な目的は、ポータブル実行(PE)ファイルを圧縮し、暗号化することです。暗号化されたペイロードは実行時に解凍され、次に解凍コードが新たに解凍されたコードの実行に移ります。マルウェアの作成者側からみて、パッカーは、バイナリの静的解析をより困難にすることによって検出を回避するのに役立つのです。Emotetのパッカーは多態型なので、シグネチャーベースの検出ツールでは、パッカーの痕跡に基づいたサンプル分析がさらにややこしくなります。
PEファイルを見てみましょう。そのリソース(.rsrc)部分は、全ファイルサイズのかなりの部分を占めていて(51%)、これが、マルウェアが圧縮されていることの印になります。
リソース部分を見ると、EXCEPTとCALIBRATEと呼ばれる2つの特異なリソースがあるのがわかります。EXCEPT のエントロピー値が高く、サイズが大きいことから、これが暗号化されたペイロードであることが推測されます。このリソースを出力してみると、暗号化されたデータがあることが確認できます。いくつかのサンプルでは、復号化されたPEファイルが、.dataの部分から投入されたことがわかりました。
Emotetはモジュール形式であり、解析を回避するために、サンドボックス検出などの解析への対策技法を使っています。解凍されたEmotetのバイナリには、数百にのぼる関数が入っています。疑わしい圧縮サンプルをGhidraのような逆アセンブラーで開いても、ほんの一握りの関数しか特定できません。これが、バイナリが圧縮されているという、もうひとつの印です。
パッカーコードの解析中、ある文字型配列を生成し、Conditional While(true)の無限ループを持つ関数に気づきました。私たちはこれを見つけて、無限ループをきっかけにして解凍コードの実行を食い止め、それによってメインのEmotetのペイロードを実行させないようにすることができないかと興味を持ちました。この関数は、RegOpenKeyA への呼び出しを通じてWindows レジストリ キーを読み取ることで動作します。このキーが見つからなければ、マルウェアは無限ループに入ります(図5)。
関数FUN_00401a90が値「interface\{aa5b6a80-b834-11d0-932f-00a0c90dcaa9}」でストリングを解読し、これがパラメータとしてRegOpenKeyA に渡されます。このレジストリキーは、Windowsのスクリプトエンジン・インタフェース、IActiveScriptParseProcedure32の動作に必要です。具体的には、このインタフェースが、与えられたコード手順を構文解析し、その手順を名前空間に追加するのです。
他のEmotetのサンプルにも同様の関数がないかを調べてみました。面白いことに、すべてのサンプルを実行すると、このレジストリキーがなければ、メインスレッドはEXIT、つまり強制終了するか、無限ループに入るのでした。
15.exeが圧縮されたEmotetのバイナリであるかもしれないという疑いを確かめるためにこれをx64dbgの中で開き、マップされたメモリ領域をチェックしてみましょう。15.exeの任意のヘッダーで、アドレススペースのレイアウトrandomisation(ASLR)が無効となっていますが、これは、可能であればモジュールにとって好ましいベースアドレス0x00400000でメモリにロードされることを意味します。
インポートされた15.exeの関数のひとつに、VirtualAllocEx があります。この関数は、メモリをリモートプロセスに割り当てるために使われ、マルウェアがプロセス侵入手段として使うこともよくあります。まずブレークポイントをVirtualAllocEx の戻り値アドレスに入れてみましょう。
ブレークポイントまで実行すると、Emotetが0x00220000Ifでメモリの割当てをしているのがわかります。 Emotetはその後、0x00422200(ファイルオフセット0x0001FE00)のマップされたイメージの.data領域から、コードスタブを新たに割当てられたメモリ領域にコピーし、それに対する制御を行います。
Emotetは次に、0x00220000にコピーされたコードからAPIとDLLの名前の難読化を解除します。
その後、kernel32.dllからGetProcAddressを呼び出し、デコードされたAPI名のアドレスを取得します。
まず、このようにLoadLibraryExAのアドレスが取得されます。次に、このアドレスを使って、0x766D0000のアドレス領域に、kernel32.dll をロードします。その後、これを手がかりにロードされたモジュールに行き、下記の関数リストのGetProcAddressを呼び出します。
ひとつ注目に値する興味深いことがありますが、それは、Emotetが mknjht34tfserdgfwGetProcAddress と呼ばれる無効なAPIのためにGetProcAddressを呼び出していることです。これは無効なので、この関数が0000007F (ERROR_PROC_NOT_FOUND)のエラーコードと共に無効の値を返します。私たちが調べたEmotetのサンプルでは全て、この無効な関数名のためにGetProcAddressに呼び出しが行われていました。
コードスタブが関数アドレスを取得すると、.rsrcセクションからではなく、15.exeの.dataセクションから判読されたPEファイルを書き込む別のメモリ領域を割り当てるためにVirtualAllocが呼び出されます。
実行ファイルを出力して調べてみると、また別の圧縮されたEmotetバイナリがあり、その中にメインのペイロードが入っているのがわかります。いくつかのEmotetのサンプルでは、最初にマップされた解読済みの実行ファイルは、メモリからこれを出力した後で直接実行することができませんが、このサンプルは実行できました。
Pestudioが、インポートがないこと、パッカーのシグネチャー「Stranik 1.3 Modula/C/Pascal」の検出、そしてこのファイルに他のファイルが入っているかどうかなど、このファイルに関する疑わしい特徴をいくつか指摘します。
0x00240000で実行ファイルを書き込み、暗号化した後、コードスタブはVirtualAllocExを使って0x00260000のアドレスに他のメモリ領域を割当てます。メモリ割当て後、メモリ領域0x00240000からペイロードを読み込み、それを0x00260000に書き込みます。
0x00260000でメインのEmotetペイロードを書きこんだ後、コードスタブはフックとJMP命令をコードに挿入します(図28)。Emotetはこうして、コード解析をより煩雑にし、逆アセンブラーを混乱させるのです。
フックが挿入されると、ペイロードは他のメモリ領域に依存して実行するようになるので、PEファイル領域のアライメントや列オフセットを固定した後でも、ディスクへの出力によってそれを実行することはできません。
ペイロードが0x00260000で変更され、準備が整うと、スタブは0x00400000からunmap 15.exe へUnmapViewOfFileを呼び出しますが、これは、最初にEmotetのイメージがロードされたメモリ領域です。 その後、0x00260000のペイロードと同サイズ(15000バイト)のメモリ領域が新たに0x00400000に割当てます。新たなメモリ領域が割当てられた後、変更されたペイロードが0x00400000にコピーされます。これはプロセス侵入テクニックであり、マルウェアはメモリ内で自身のバイナリを変更して上書きします。
ペイロードを0x00400000にコピーした後、Emotet はAPI名を解析し、実行フローをペイロードに転送します。この場合、実行フローを転送するのは0x0040C730ですが、これは次に、API名と一致するハッシュを解析する関数を呼び出します。
マルウェアの相関性を見ることができるストリング法が難読化されているので、メインのEmotetペイロードによって、解析者がコードフローを追跡するのが難しくなります。
API名の解析後、Emotet実行プロセスのプロセスID(PID)を取得するためにGetCurrentProcessIdが呼び出されます。その後、Emotetはすべての実行プロセスを反復し、自身のモジュール名と親PIDを探します。自身の親PIDを見つけたら、PEM%X 形式で2つのミューテックスを作成します。ミューテックスの一方は親プロセスID(PEM[PRID])を使って作成され、もう一方は自身のPID (PEM[PID])を使います。
これらのミューテックスを作成した後、CreateEventW を呼び出し、PEE%X 形式でイベントを作成しますが、ここで%Xは親PIDです。ミューテックスが2つともうまく作成できたら、再び同じパスから15.exeを開始します。子プロセスを開始した後、PEE%Xイベントで WaitForSingleObject を呼び出します。
Emotetのサンプルのいくつかで、コマンドラインスイッチで子プロセスを開始していることがわかりました。このコマンドラインスイッチは、Emotetプロセスが子プロセスとして開始されていること、指定されたタスクを実行しなければならないことを示す指標です。
開始された子プロセスは、上記の2つのミューテックスを作成するかどうか判断できるまで、まったく同じことを行います。今度は、ミューテックスPEM[PPID]のためのCreateMutexの呼び出しは、「ERROR_ALREADY_EXISTS」のエラーを返して失敗します。 子プロセスでのミューテックス作成が失敗した後、イベントPEE[PPID]の信号を親プロセス15.exeに送ります。親プロセスは、待機状態からExitし、みずから終了します。
次に、起動された子プロセスは「ipropmini」というサービスを作成し、コマンド・アンド・コントロール(C&C)通信を確立します。
Emotetは、メインのペイロードを解凍するために多層アプローチを使っています。マップされたイメージを変更した後、自己侵入テクニックを用いてメモリ内でペイロードを実行します。ペイロードはファイルシステムに書き込まれることがなく、フックとJMP命令があるため、アンチウィルスや動的コード解析に基づいた検出を簡単に回避することができるのです。
Emotetのドロッパーは、下記のレジストリキーへの読み取りアクセスを遮断することによって食い止められます。ドロッパーは無限ループに入るか、プロセスを終了するので、ペイロードが実行される前に条件分岐が発生し、システムの統合性に害を及ぼしません。
Emotetドロッパーは、次の方法で検出できます。
Author : Ratnesh Pandey
監訳:ブロード