本当にネタがあるときしか更新しないよ系ブログの久々の記事です。
ネタが無かったというよりは、マルウェア解析が順調すぎて特に詰まるところが無く、一方で特に特記するような内容もない、というのが本当のところですかね。時間があまり取れなくて進捗良くないってのもありますが。
今回のマルウェアは某サイトのアカウントから取得したマルウェアです。
ハッシュ値は以下のとおり。
MD5:
545BFDC9B1976AE0003443FF4F90EB7E
SHA1:
92E8CE006BB3C4A1DDB8D8BA8DE3A90C0BBB6326
一応、VirusTotalでサーチすると、検知名はEmotetとなっています。
ただし、コイツはパッカーは相当凝っていたけど、本体は恐ろしくショボい(ただし、一応まだ解析中なので、後でスゴいのが出るのかもしれない・・・)ので、本当に世界を震撼させているマルウェアかどうか謎です。
(もしこれをもって「凄いハッカーだ」と言うのなら、ハッカーとやらもセキュリティの大家とやらも相当チョロいんじゃないかってくらいホントにショボいマルウェアです。これマルウェアっていうよりCTFのネタでしょ。)
ああ、でもWannaCryも中身は相当の雑魚だったので、風評はアテにできないか。
(おかげ様でGIACのGoldはおいしくいただけたのですけどねぇ(ゲス笑い))
今回は、このマルウェアの解析メモです。
簡単な仕組みの解説と、IoCになるんだかならないんだかの情報です。
(まあ、主に自分の覚え書きまたは今後の課題。)
パッカー部
パッカー部はかなり秀逸で、これのために解析に時間がかかったり、場合によっては本体にたどり着けなかったりするんじゃないかと思います。
むしろ、「Emotetの本体はこっちで、このパッカー使ってるのは全てEmotetだ!」というなら納得いく出来といえるくらいです。
使っているギミックのうち、コードの関数単位での暗号化とHeaven's Gateは特に厄介で、当面はこれを利用された検知・解析に悩まされるんじゃないかと思います。特に、自動検知、自動解析はちょっと厳しいんじゃないですかね。
個人的に気になるのはこのパッカーの普及状況で、サイバー犯罪者内で広く利用されるようになってきているのか、特定のAPTグループの作成・利用なのかによって、今後の対処が変わるんじゃないですかね。
使われたていたテクニック
- コードの関数単位での暗号化(xor 使用)
- Heaven's Gate(32ビットコードから64ビットコードの実行)
- 特定文字列の32ビットハッシュ化(joaatハッシュ)
- 32ビットコードで64ビットのsvchost.exeを起動し、Heaven's Gateで実行する64ビットコードでsvchost.exeに本体をインジェクションし、コードを実行(Process Hollowingではない)。
- MFCで開発したか、少なくともMFCのモジュールを利用して動くようになっている(今時珍しい気はする)。
- データをBASE64らしき符号化をしている箇所がある。
- データをXORで暗号化している箇所がある。
- WSReset.exeを実行したときに自身が実行されるようにレジストリを改変し、WSReset.exeを実行して自身を再起動する。
テクニックのうち、特記すべきものは上の2つですかね。
関数単位での暗号化は、プログラムを実行した後にメモリダンプをしても、コードの大半が暗号化されているため、マルウェアの判定や解析ができないでしょう。サンドボックス解析や手動の解析でコードをトレースする必要があります。
Heaven's Gateは、サンドボックス解析や手動解析を途中で途切れさせ、解析を困難、または時間がかかるようにするためには有効だと思われます。特に64ビットコードから別コードへのインジェクションを行っているため、追跡しきれない製品や解析者がいると思われます。
(要らないと思うけど、他も一応、私の感想)
joaatハッシュの利用は、ASCIIだと解析者が簡単に読めて解析のヒントになるから、難読化する目的なんじゃないかと思います。
もっとも、ハッシュ関数がバレてるので、レインボーテーブルでも作っておけば割とヒットするんじゃないかなぁ。この検体はソルトも使ってないので。
svchost.exeへのインジェクションは、メモリフォレンジックやプロセス監視の際に、あからさまに不審な実行ファイルが動いていると目立つから、それを隠す目的だと思います。
意外にも、よく使われるProcess Hollowingはせず、作成したsvchost.exe上にリモートでメモリを確保して本体をインジェクションしていました。
目新しいのは、32ビットの実行コードでsvchost.exeを普通に起動すると32ビットのsvchost.exeが起動するハズだが、WOWを駆使して64ビットで起動させていたことかな。
その後Heaven's Gateで64ビットコードを実行している時にsvchost.exeを起動すれば64ビットになるんじゃないかとも思ったが、何らかの不都合でもあったのだろうか?(Heaven's Gateで64ビットでコードを実行していても、起動が32ビットだと32ビットと判定されるのかもしれないが、未検証)
MFCの利用は、別に使ったからといってどうというわけでもないんだけど、単に珍しいなーと。
ああ、MFCの解析の場合、コールバックに癖があるから、初見だと「あれ?」ってなるかもしれない。
MFCの仕様はまあともかく、コールバックの仕組みが分からないなら、マルウェア解析の前に勉強することがあるんじゃないかなって程度のお話。
そーいや、WannaCryの身代金を払った後に動くハズだったダイアログベースの復号画面、あれもMFCだったなぁ(本当にどうでもいい情報)。
BASE64らしき符号化も、目的はjoaatのハッシュと同じ難読化だと思います。違うのは、こちらは可逆で、元に戻す必要があるデータの符号化目的だと思います。「らしき」としているのは、素読みでそれっぽいと判断はしましたが、実際にデータをBASE64の処理にかけてマッチするかまでは検証していないからです。その結果のデータを何に使ってたっけ・・・ってくらい、メモに残ってる程度の印象の薄い処理でした。
データのXORでの暗号化は、難読化の定番で今更言うまでもないとは思います。
鍵を変えることでデータが変わるため、難読化後のデータでのシグニチャや検体そのもののハッシュ値では検知できなくなるためだと思います。
WSReset.exeを実行したときに自身が実行されるようにレジストリを改変は、特定の条件の場合自身を再起動していますが、直接自身で別プロセスをキックしているのではなく、WSReset.exeを実行時に起動するようにレジストリに設定しています。
WSReset.exeはWindows Storeの初期化プログラムらしく、起動しても目立たず、影響も小さいためユーザに気づかれにくいから使ってるのだと思います。マルウェアがこのプロセスを作成した場合は追跡されると思いますが、WSReset.exe実行時にレジストリの設定により起動されるプログラムまでは追跡されにくいため、サンドボックスや解析者を撒くために使ってるのかもしれません。
まあ、条件判定一発ゴマかせば抜けるんですけどねぇ。
特徴のある情報・痕跡
- 特定の文字列でjoaatハッシュ化された値(他にもあると思う)
- ntdll.dll = [7B B8 16 EA]
- advapi32.dll = [DE DC 9A FF]
- KERNEL32.dll = [12 73 F0 FE]
- shell32.dll = [8C 76 99 D5]
- ole32.dll = [D4 E6 69 95]
- レジストリの改変結果
- キー:HKEY_CURRENT_USER\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\shell\open\command\DelegateExecute
- 値:(空白)
- キー:HKEY_CURRENT_USER\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\shell\open\command
- 値:C:\Windows\system32\cmd.exe /c start (マルウェア実行ファイルのフルパス名)
難読化や暗号化といったアンチフォレンジックを多く採用しているだけあって、なかなかこれといった目立つ値が見当たりません。
この検体のパッカーでもっとも痕跡が残るのがレジストリ。
WSReset.exeに紐づくIDの「shell\open\command」にコマンド実行コードを埋めることで、WSReset.exe実行時にマルウェアが実行されるようになっています。
このギミックを実現させるためにはレジストリを書き換える必要があるため、このギミックを使う限りレジストリに痕跡が残ります。
もっとも、自動起動とは異なり目立たないところにあるため、見落としがちかもしれない。
ただし、将来的にこのマルウェア(というかパッカー)の亜種が出た時に、WSReset.exe以外のプログラムをトリガーにする可能性もあります。その場合、このレジストリのパスも変わります。
HKEY_CURRENT_USER\Software\Classes\(ID)\shell\open\commandに、ホワイトリストにない実行形式ファイルを実行するようなコードが埋まっていた場合、それをマルウェアの可能性があると判断することで発見できる可能性はあるかと思います。
パッカー部は、やはりテクニックに特徴があるので、振る舞い検知、サンドボックスといった動きからの検知のほうが検知できる可能性は高そうではあります。
静的解析では、もうエントロピーの高さで疑うくらいしかできなさそうです。
暗号化されているプログラムからシグニチャを見出そうとするのは不毛なのでやめておいた方がいいだろう、と使われていたアンチフォレンジック技術からも推察できます。
デバッガでの解析のポイント
関数単位の難読化は、デバッガで解析する場合に注意が必要です。
ソフトウェアブレークポイントで問題が発生しやすいためです。
そのため、ハードウェアブレークポイントを活用する必要があります。
まあ、そもそも先行してブレークポイントを設定しようとしても、コード全体がほとんど読めないんですけどね。
このギミックは、呼び出し側の関数が、中継の関数を経由して、この関数が暗号化された関数を復号化して呼び出します。
このとき、中継している関数は、呼び出し元も暗号化してしまいます。
呼び出し元のコードにソフトウェアブレークポイントを設定している場合、元のコード部分を「INT 3」に置き換えていますが、この状態で暗号化されると、本物のコードではなく、ソフトウェアブレークポイントの「INT 3」のまま暗号化されてしまいます。
一応、この状態でも、ブレークポイントを何もいじらなければ、関数がリターンしてくる際に再度復号化されて丁度ブレークポイントが設定された状態に戻りはします。
ただし、うっかりブレークポイントを解除してしまったりすると、このコードが滅茶苦茶になり、正常に動作しなくなります。
(ここの仕組みの詳しい説明は、それだけで記事一つ書けそうだなぁ・・・)
これを回避する手段として有効なのは、ハードウェアブレークポイントの活用です。
ハードウェアブレークポイントは、コード自身に手を加えないため、暗号化、復号化の影響を受けないためです。
ただし、ハードウェアブレークポイントには、数に限りがあります。
ただ、IDAを使っている場合は、ハードウェアブレークポイントが使える数以上になると、IDAがハードウェアブレークポイントをエミュレートしてくれます。このため、IDAを使っている場合は、ハードウェアブレークポイントを活用することで問題を回避しやすいです。ハードウェアブレークポイントのエミュレートは便利ですが、その分速度が遅くなったりする影響は出るようなので、その点は留意は必要で、不要になったブレークポイントはマメに外したほうが良いようです。
Heaven's Gateは、以前の記事で書いたとおり、セレクタ0x0033を指定してfar callすることで、32ビットコードから64ビットコードを実行できるようにする方法です(詳細は旧記事参照)。
少なくともIDAではこれをエミュレートしておらず、例え64ビット版のIDAを使っていても、このfar call以降を32ビットで実行しようとしてしまい、上手くいきませんでした。
このケースでは、呼び出されるコードは「MZ」ヘッダで始まる通常の実行ファイルデータをメモリに展開したものだったため、これを抽出することで64ビットの実行ファイルにすることができ、これを64ビット版のIDAで解析することで処理の流れの解析は可能です。
ただし、この処理はsvchost.exeにインジェクションをする際、32ビットコードで作成した64ビットのsvchost.exeプロセスのプロセスIDとスレッドID、32ビットコードで解凍されたインジェクションコードを参照しています。
抽出した64ビット単独ではこれらのデータがないため、色々工夫してサスペンドされている64ビットのsvchost.exeのプロセスIDとスレッドIDを取得したり、32ビットコードで解凍されたインジェクションコードを64ビットのコード上に展開しないと、64ビットのsvchost.exeにインジェクションされたこのEmotetらしきマルウェアの本体を実行させて解析することはできません。
ちなみに、私はものすごくカッコ悪い泥くさい方法でクリアしました。
あまりに酷いんで記事にはしてないんですけどね(多分ニーズがない)。
(もっとも、本人が同様の解析するときには平気で使うんだろうなぁ。)
なお、以上は意地でもIDAを使っちゃうケースなので、他のデバッガを使って生成された64ビットのsvchost.exeにインジェクションされたコードにトラップする方法があるかもしれません(誰か教えて?)。
MFCの利用では、フレームワークからコールバックで呼ばれて動く形式になります。
そのため、MFCのジャンプテーブルがあるはずなので、それを分析するのが正規だとは思います。
ただ、それを勉強するのも分析するのも面倒だったので、めぼしい関数の開始位置にブレーク貼ってやりました。
これで、コールバックで関数が呼ばれたらブレークしてくれます。
ただし、デバッガにヒットしてデバッガに制御がいくと、その処理の後毎回MFCが再描画イベント等を発生させてしまい、毎回それにヒットしてしまって処理が進まなくなってしまいます。
このため、不要なブレークにヒットしたら、外していくことで処理を進める必要があります。
なお、この検体では、CDialog::DoModal(void) でコールバックにより呼び出された関数がメインルーチンの入り口でした。この検体では、アドレスは0x00405530でした。
(「画面表示しないのにDoModalするの?」とかイワナイ。私がツッコミたいくらいだわ。)
WSReset.exeの実行は、そのルートに行くと、レジストリにWSReset.exe実行時に自身を実行するような細工をしたあと、ShellExecuteExでWSReset.exeを実行して自身の別プロセスを生成したのち、自身をExitProcessで終了してしまいます。
これで起動されるプロセスに対してアタッチするのも現実的ではないので、条件判定一つにフェイクをいれて継続して解析したほうが早いと思います。
まあ、特定のレジストリに実行したいプログラムのパスを登録すると、それに関連するプログラムの起動時に登録されたプログラムが実行される様子は、OSにこんな機能があるんだということを確認できるので、知らない方は一度動かしてみるのは後学にはなると思います。
他のテクニックは、通常どおりロジックの参照とパラメータの追跡で基本的にはいけます。
ただし、関数単位の難読化でコードが暗号化されていることも多いので、復号化されたときにコードをよく確認しておいたほうが良いでしょう。