2020年10月12日

なぜ最近のEmotetは検知が更に難しくなっているのか - パート2

現在世界最大のマルウェアボットネットであるEmotetは、2014年頃から活動を始め、業界でも最も困難な脅威の1つであり続けています。このボットネットは、感染したシステムにランサムウェアや情報搾取マルウェアをばら撒くことで莫大な被害をもたらします。最近では、フランス、日本、ニュージーランドでEmot

現在世界最大のマルウェアボットネットであるEmotetは、2014年頃から活動を始め、業界でも最も困難な脅威の1つであり続けています。このボットネットは、感染したシステムにランサムウェアや情報搾取マルウェアをばら撒くことで莫大な被害をもたらします。最近では、フランス、日本、ニュージーランドでEmotetの感染数の増加が確認されています。感染数の多さは、Emotetのマルウェアがセキュリティ製品によって検出されにくい状態が続いていることを示しています。

Emotetの前回の攻撃の波は2019年9月に始まり、2020年2月に終了しました。5ヶ月間活動を休止し、2020年7月17日に新しい攻撃の波が始まりました。Emotetの背後にいる作成者は、より多くの組織が感染するにより、そのバージョンのEmotetの検出に成功するようになっていくことを認識しています。必然的に、業界が脅威に対する理解とコントロールを深めるにつれて、この攻撃の波は下がり始めます。

マルウェア作成者が研究室に戻る必要があることに気付いた時、ターニングポイントが訪れました。一定期間、彼らは次の攻撃に備え始め、それを可能な限り強力で検知回避できるものにするため、完全に姿を消しました。その戦術は今のところうまくいっているようです。数ヶ月間の沈黙の後、Emotetは以前よりも優れた検知回避能力を身につけて再登場しているように見えます。

前回の記事では、Emotetローダーを調査しました。私たちは、セキュリティ製品を欺く良性コードを挿入して悪意のある機能を見えにくくすることで、どのように検出を回避しているかを明らかにしました。このローダーには、解読されて実行される隠しペイロードが含まれていました。このペイロードは、ローダーには見られなかった新しい回避テクニックを使用しており、開発者がマルウェアを生成するために使用しているgitリポジトリへと導いてくれるユニークな指標をいくつか持っています。

現在の攻撃の波が始まった2020年7月17日から9月3日まで、cryptolaemus が公開したレポートを収集しました。この情報によると、44万4000件のユニークなEmotetローダーが作成され、ピークは8月末に発生しています。

photo1-1000x592.png

このブログ記事では、ローダー内部で暗号化されたペイロードを調査し、感染プロセスの次のステップを分析し、このマルウェアの分析を困難にするために使用されたテクニックを説明しています。

シェルコード

マルウェア実行の最後のステップでは、メモリバッファが確保され、復号化されたペイロードがそこにコピーされました。ペイロードは、シェルコード、PEファイル、文字列「dave」を含むオーバーレイで構成されています。シェルコードは、以下のステップを実行することで、PE ファイルの反射型ローディングを行っています:

  1. PEB 構造体を読み込んで、LoadLibrary、VirtualAlloc、VirtualProtect などの機能に必要な API を探す
  2. オプションのヘッダの SizeOfImage の値に応じてメモリバッファを割り当て
  3. 画像のヘッダーをコピー
  4. 画像セクションをコピーし、セクションの特性に応じてメモリ保護を変更
  5. IATに記載されている機能の正しいアドレスを取得
  6. 再配置表に従って再配置を実行
  7. エントリーポイントへのジャンプ
  8. 名前がハッシュ0x30627745に変換できる関数を検索し、オーバーレイから文字列 "dave "で呼び出し

このシェルコードは、メモリフォレンジックを使って検出するのをより困難にする方法で PE ファイルをロードする悪性の高い技術を利用しています。このシェルコードは、"MZ" や "This program cannot be run in DOS mode" のような文字列を削除するために、DOS ヘッダをゼロに置き換えます。これらの文字列は、メモリに注入されたPEファイルを検出するために使用できてしまうためです。

メモリの比較: 左がシェルコードで読み込まれた後の PE ファイルで、右がその様子

このシェルコードにはいくつか変わった点がありました:

  1. シェルコードはDLLではなく、エクスポートも含まれていないにもかかわらず、ファイルのエクスポートテーブルを検索して呼び出したい関数を探していました。
  2. 文字列 "dave" が場違いで意味がないように見えました。
  3. 見つかったハッシュアルゴリズムはシェルコードの間では一般的なもので、コードで使われている WinAPI のハッシュはオンラインリポジトリで見つけることができますが、エクスポートされた関数のハッシュ (0x30627745) はそこには現れませんでした。

これらの指標を検索すると、このシェルコードのソースコードが含まれている git リポジトリにたどり着きました。デフォルトでは、シェルコードはロードするファイルの中から "SayHello "という名前のエクスポートを検索し、"dave "はこの関数に送られるパラメータであることがわかりました。Emotetグループはスクリプトのデフォルトパラメータを変更しておらず、そのIOCがソースコードにつながっていました。

シェルコードがどのように動作するかわかったので、ロードされたPEファイル、つまりローダーの最終的なペイロードの話に移りたいと思います。

 

最後のペイロード

ローダから抽出されたデータからシェルコードとオーバーレイ文字列を削除すると、このような PE ファイルになりました:

D59853E61B8AD55C37FBA7D822701064A1F9CFAF609EE154EF0A56336ABA07C1

良性のファイルとして偽装されたローダーに比べて、このファイルは明らかに悪意のある特徴を持っています:

  1. コード難読化 - マルウェアの開発者は、コードの流れを理解しにくいように複雑なスイッチケースを使用しています。このファイルには不必要なジャンプ命令が多く含まれており、デバッグプロセスが長くなります。

photo3-1000x731.png

Graph overview of the main function generated using IDA, showing the code obfuscation.

  1. 文字列なし - サンプルの静的分析を行った結果、意味のある文字列は見つかりませんでした。これは、このマルウェアには暗号化された文字列が含まれており、それらが使用された場合にのみ復号化されるためです。サンプルをデバッグしたところ、永続化に使用されるレジストリキーやHTTPリクエストに使用されるフォーマット文字列など、興味深い文字列が見つかりました。
  2. 不審なAPIコールを避ける - 多くのマルウェアがAPIコールを悪用しているため、一部のAPIコールはセキュリティ製品によって監視されていることが知られています。マルウェアは、自分自身をインストールできるシステムの深さを判断するために、管理者権限で動作しているかどうかを知る必要があります。そのためのシンプルな API (IsUserAnAdmin) を使用する代わりに、管理者権限でのみアクセス可能なサービスコントロールマネージャへのフルアクセス権を持つハンドルを取得しようとします。この方法は不審さが少なく、セキュリティ製品が使用する特定のヒューリスティック検出を回避するのに役立つかもしれません。
  3. 空のインポートテーブル - マルウェアは、IATにAPI関数をリストアップしないことで、その機能と意図を隠しています。このような場合は静的分析を行っても、マルウェアの機能や動作を判断するのに役立ちません。その代わりに、マルウェアはWinAPIアドレスを動的に取得します。

以前に議論したシェルコードのように、それは PEB 構造を読み取りますが、独自のハッシュアルゴリズムを使用していますので、WinAPI のための標準的なハッシュ辞書は、コードが使用するハッシュを文字列に変換するのに役立ちません。コードを理解しやすくするために、独自の辞書を作成する必要があります。

分解して見ると、ハッシュアルゴリズムは複雑に見えますが、多くの数学的な演算が含まれており、互いに打ち消し合っています。IDAの擬似コードユーティリティを使って、簡単なC言語のコードに変換します。このコードを使って、多くのDLLファイルやAPI名をハッシュ化した辞書を作成しました。コード中のハッシュの横にDLLファイル名とAPI名を追加するために、IDAPythonを使ったスクリプトを書きました。

<span style="color: #0000ff;">import</span> idautils
<span style="color: #0000ff;">import</span> json

hashes_dict = json.load(<span style="color: #0000ff;">open</span>(<span style="color: #993366;">"emotet_hashes.json"</span>, <span style="color: #993366;">'r'</span>))
dlls_set = <span style="color: #33cccc;">set</span>()
apis_set = <span style="color: #33cccc;">set</span>()
<span style="color: #0000ff;">for</span> func in idautils.Functions():
    flags = idc.get_func_attr(func, FUNCATTR_FLAGS)
    <span style="color: #339966;"># skip library & thunk functions</span>
    <span style="color: #0000ff;">if</span> flags & FUNC_LIB or flags & FUNC_THUNK:
        <span style="color: #0000ff;">continue</span>
    dism_addr = <span style="color: #33cccc;">list</span>(idautils.FuncItems(func))
    <span style="color: #0000ff;">for</span> ea in dism_addr:
        <span style="color: #0000ff;">if</span> idc.print_insn_mnem(ea) == <span style="color: #993366;">"mov"</span>:
            register = idc.print_operand(ea, 0)
            value = idc.print_operand(ea, 1)
           <span style="color: #339966;"> # ecx contains the hash respresenting the dll</span>
            <span style="color: #0000ff;">if</span> register == <span style="color: #993366;">'ecx'</span>:
                <span style="color: #0000ff;">if</span> value in hashes_dict[<span style="color: #993366;">"dlls"</span>]:
                    dll_name = hashes_dict[<span style="color: #993366;">"dlls"</span>][value]
                    dlls_set.add(dll_name)
                    idc.set_cmt(ea, dll_name, 0)
            <span style="color: #339966;"># edx contains the hash respresenting the API</span>
            <span style="color: #0000ff;">elif</span> register == <span style="color: #993366;">"edx"</span>:
                <span style="color: #0000ff;">if</span> value in hashes_dict[<span style="color: #993366;">"apis"</span>]:
                    api = hashes_dict[<span style="color: #993366;">"apis"</span>][value]
                    apis_set.add(api)
                    idc.set_cmt(ea, api, 0)
<span style="color: #0000ff;">print</span>(f<span style="color: #993366;">"{len(dlls_set)} DLLs and {len(apis_set)} APIs were found"</span>)
<span style="font-size: 8pt;">隠しインポートをコードに追加するIDAPythonスクリプト</span>

 

このマルウェアの目的は、感染したシステムに関する情報を C2 サーバーに送信し、コマンドだけでなく実行するための他のファイルを受信することです。ファイルにはIPアドレスのリストが含まれており、サーバーの1つから応答が届くまで繰り返し実行されます。

マルウェアは、コンピュータ名や実行中のプロセスなどの情報を収集し、AESを使用してデータを暗号化し、HTTPで送信します。サーバーは、それのリクエストに対して、感染したシステム上で実行されるランサムウェアや情報窃盗などのマルウェアを送るのです。

まとめ

このブログ記事では、Emotetマルウェアの実行の次のステップを分析しました。攻撃者が公開されているシェルコード生成器を使用していることがわかりましたが、これには固有の識別子が含まれていました。次に、C2 サーバーと通信し、追加ファイルをダウンロードするローダーの最終ペイロードを調べました。コードを分析されにくくするために様々な技術が使われていましたが、解読可能なコードにする方法を示しました。Emoteマルウェアによる感染のプロセス全体を観察することで、このボットネットがどのようにしてこのような長期間にわたって検知されないように進化しているかを垣間見ることができます。