Emotetのコマンド&コントロールのケーススタディ

By and

Category: Unit 42

Tags: , , , , ,

A conceptual image representing the concept of malware, such as that covered in this case study of Emotet Command and Control traffic.

This post is also available in: English (英語)

概要

2021年3月8日、Unit 42はブログ「攻撃チェーンの概要: 2020年12月および2021年1月のEmotet」を公開しました。この分析から、更新されたバージョンのEmotetは、さまざまなコマンド&コントロール(C2)サーバーと通信してデータ漏出やさらなる攻撃を行うことがわかりました。私たちは、攻撃者がユーザーの機密情報窃取や新たなペイロードのドロップのために、高度な回避技術と暗号化アルゴリズムを利用してC2サーバーと通信し、被害ネットワーク環境やプロセスを調査している様子を観測しました。

本稿では、メインロジックの開始部分からはじめ、暗号化メカニズムをカバーし、C2データがHTTPプロトコルを介してC2サーバーに漏出されるまでの過程を順を追って技術的に分析していきます。

パロアルトネットワークスの次世代ファイアウォールをご利用のお客様は、脅威防御WildFireなどのセキュリティサブスクリプションを通じてEmotetから保護されています。Cortex XDRをお使いのお客様も同脅威から保護されています。

技術的分析

この分析では、通常のIDA Proの関数の書式(sub_*)を置き換えるカスタム関数名(たとえばcollect_process_data)を使用しています。また、イメージのベースアドレスが0x2E1000である32ビット(x86)DLL実行可能ファイルを想定しています。参照しやすいように関数オフセット、名前、カスタム名を次の図に記載しておきます。

注: 使用されたサブルーチンはここに提示した関数オフセットから簡単に見つかるのでこの図には記載していません。

図1 IDA関数の参照情報
図1 IDA関数の参照情報

今回の分析は、エントリポイントとなる関数c2_logic_epsub_2E2C63)から見ていきます。

暗号化API関数

このマルウェアは、2つの主要関数encryption_functions_oneencryption_functions_twoを使用します。どちらの関数もMicrosoftのBase Cryptography(CryptoAPI)を使っています。以下、マルウェア実行中にこれらの暗号化API関数が使用するプロパティと実行するアクションを記載します。

  • CryptAcquireContextW: PROV_DH_SCHANNELをプロバイダタイプ(0x18)として使います。CRYPT_VERIFYCONTEXTフラグとCRYPT_SILENTフラグとのビット単位OR演算(0xf0000040)を行ってユーザーインターフェイス(UI)がユーザーに表示されないようにします。
  • CryptDecodeObjectEx: メッセージエンコードタイプX509_ASN_ENCODINGPKCS_7_ASN_ENCODINGとをビット単位OR演算(0x10001)したもの、X509_BASIC_CONSTRAINTS0x13)の構造体タイプ、デコードされる合計0x6a分のバイトを使います。
  • CryptImportKey: CUR_BLOB_VERSION0x2) バージョンでサイズ0x74バイト、タイプPUBLICKEYBLOB0x6) のキーブロブをインポートします。
  • CryptGenKey: 値にCALG_AES_1280x0000660e)を設定したALG_IDを使用して128ビットのAESセッションキーを生成します。
  • CryptCreateHash: 値にCALG_SHA0x00008004)を設定したALG_IDを使用してその名の通りSHAハッシュアルゴリズムを設定します。
  • CryptDuplicateHash: 複製されるハッシュへのハンドルを受け取ります。
  • CryptEncrypt: この関数はCryptGenKey関数によって生成された暗号化キーへのハンドルと、CryptCreateHashによって生成されたハッシュオブジェクトへのハンドルという2つの主要パラメータを受け取ります。この値が、暗号化後のCryptEncrypt関数呼び出しと、パラメータとしてのC2データへのポインタ渡しに使用されます。
  • CryptExportKey: SIMPLEBLOB0x1)タイプとCRYPT_OAEP0x00000040)をフラグとして使用します。キーブロブがエクスポートされるバッファへのポインタは、マルウェアのC2データの一部です。
  • CryptGetHashParam: CryptExportKey関数同様、宛先ポインタはマルウェアのC2データの一部です。
  • CryptDestroyHash: その名が示す通り指定されたハッシュを破棄します。

コンピューター IDの生成と長さのチェック

generate_machine_id関数は、その名が示す通り、感染端末のコンピューター ID生成をになっています。コンピューター IDの生成には_snprintf関数の呼び出しが使われます。この関数はフォーマット文字列%s_%08Xを使ってGetComputerNameAGetVolumeInformationWが生成した値を連結します。今回の分析で使用した検証マシンの特定ケースでは、この結果の値はANANDAXPC_58F2C41Bとなりました。

図2 コンピューター ID(machine-ID値)を生成するための関数呼び出し
図2 コンピューター ID(machine-ID値)を生成するための関数呼び出し

コンピューター IDが生成されると長さチェック検証も生成されます。これは「lstrlen」関数のラッパーであるgen_machine_id_lengthの呼び出しと、前の関数呼び出しからの戻り値をパラメーターとして渡すことで実現されています。私たちの検証マシンのケースでは、この結果の長さは「12」となりました。これらの値はC2データの一部として使用されることから特定スタック変数内に存在します。続いてwrite_GoR関数に対する新たな関数呼び出しが行われます。ただし、私たちの分析と戻り値(0x16F87C)の使われ方からは、これが何を目的とするものなのかはわかりませんでした。C2データの最後にあることから区切り文字ではないかと考えられます。

図3 C2のデータ区切り文字を生成するための関数呼び出し
図3 C2のデータ区切り文字を生成するための関数呼び出し

オペレーティングシステムのデータ収集

漏出されるデータにはオペレーティングシステム情報も含まれています。この漏出にはcollect_os_data関数呼び出しが使われていました。

図4 オペレーティングシステム情報の収集のための関数呼び出し
図4 オペレーティングシステム情報の収集のための関数呼び出し

この関数はRtlGetVersionを呼び出し、OSVERSIONINFOW構造体にデータを保存します。GetNativeSystemInfoSYSTEM_INFO構造体内にデータを保存して同じことを行います。

図5 API呼び出しによって値を設定されたOSVERSIONINFOW構造体とSYSTEM_INFO構造体
図5 API呼び出しによって値を設定されたOSVERSIONINFOW構造体とSYSTEM_INFO構造体

これらデータ構造体への入力が済むと、0x2EC3DB(戻り値)、0x2EC440(MajorVersion)、0x2EC3DB、0x2EC3D0(MinorVersion)、0x2EC45A(Architecture|PROCESSOR_ARCHITECTURE_INTEL)の各オフセット位置の命令により特定のデータがフェッチされます。

戻り値は、MajorVersionMinorVersionArchitecture、そしてNT_PRODUCT_TYPE列挙データ型のシンボリック定数(NtProductWinNT)であるRtlGetNtProductType呼び出しの戻り値(0x1)、これら固定値との加算と乗算で計算されます。次のPythonコードは、それらの値を生成するロジックをシミュレートしたものです。

図6 OSデータ生成アルゴリズムをエミュレートするPythonの概念実証(PoC)
図6 OSデータ生成アルゴリズムをエミュレートするPythonの概念実証(PoC)

リモートデスクトップサービスセッション情報の収集

現在のプロセスのプロセスIDを取得するGetCurrentProcessIdの呼び出しを含むさらに多くの呼び出しが実行され、その戻り値がProcessIdToSessionId関数にパラメータとして渡されます。MSDNの説明によるとProcessIdToSessionId関数は「指定したプロセスに関連付けられたリモートデスクトップサービスセッションを取得する」とあります。この関数の戻り値は、現在のプロセスが実行されているターミナルサービスセッションを示します。

図7 ターミナルサービスのセッションIDを取得するための関数呼び出し
図7 ターミナルサービスのセッションIDを取得するための関数呼び出し

プロセススキャンとC2データ収集

この関数はCreateToolhelp32SnapshotProcess32FirstWGetCurrentProcessIdProcess32NextWの各関数を呼び出す従来型の方法を実行することで、システム上で実行されているアクティブなプロセスを収集します。この関数に入る前に、オフセット0x2E4715にある命令がローカル変数のアドレスをEAXレジスタにロードしてスタックにプッシュします。この変数には最終的にプロセスデータ情報を受け取ることになるRtAllocateHeap関数の呼び出しにより生成したポインタが含まれています。

図8 プロセスデータを使用して値を生成・初期化するための関数呼び出し
図8 プロセスデータを使用して値を生成・初期化するための関数呼び出し

この関数はcopy_collected_data_parentという名前のサブルーチンも呼び出します。実行中、この関数はRtlAllocateHeap関数呼び出しとそれに続く複数のmemcpyラッパー関数呼び出しにより収集済みC2データを新しく割り当てられたセクションにコピーすることで、新しいメモリセクションを生成します。

図9 収集したC2データで値を初期化する関数呼び出し
図9 収集したC2データで値を初期化する関数呼び出し

次に呼び出す関数がHTTP_LAUNCHERで、これには他のタスクの中でも特にWeb機能を提供するサブルーチンが含まれています。この時点で一連の変数はそれまでに実行された関数からの対応する戻り値で初期化されています。次のASCIIダンプは、変数アドレス、関連データ、そして「どの関数ないし命令オフセットが特定のデータを提供したか」に関する情報を示しています。

図10 収集されたデータとデータを生成した関数への参照を含むスタックのスナップショット
図10 収集されたデータとデータを生成した関数への参照を含むスタックのスナップショット

次のステップはwrite_collected_dataサブルーチンを呼び出すc2_data_write関数で、これはパラメータとして次の2つの値を渡します。

  1. C2データ(0x2EAC3E)へのポインタ
  2. オフセット0x2F989BにあるRtlAllocateHeap関数の呼び出しによって生成された、新しいメモリ割り当ての戻り値(アドレス)

この新しく生成されたデータはあるアルゴリズムを通過します。このアルゴリズムでは、書き込み(オフセット0x2FA830)に加え、C2データの特定のバイト(オフセット0x2FA6DE)、特に一部のファイル名拡張子の変更も行います。

図11 収集したデータをメモリに書き込む関数呼び出し
図11 収集したデータをメモリに書き込む関数呼び出し

データが収集されるとwrite_c2_data_zeroへの呼び出しが行われ、これがAllocateHeap (0x2E99DC)関数を呼び出して追加のメモリが割り当てられます。この関数は最終的に2回呼び出され、さらにサブルーチンを呼び出します。それらのサブルーチンでは、write_c2_data_one関数のオフセット0x2F362Aにある命令が2つのDWORD値(固定値の0x1、C2データ長の0x132)を生成します。次のステップはcopy_c2_data(オフセット0x2F794Cにあるmemcpyへのラッパー)関数への呼び出しです。これは、C2データを前述の2つの値の隣の新しい場所にコピーします。

図12 中間C2データコピーを実行する関数呼び出し
図12 中間C2データコピーを実行する関数呼び出し

次のシーケンシャルな関数実行は、CryptDuplicateHashへの呼び出しです。この後、copy_binary_dataへの呼び出しが行われ、それによって最終的なC2データが新しいメモリ割り当てにコピーされます。この場所にはこのあとの後続のステップでCryptEncrypt関数が暗号化する前の最後のC2データが含まれています。

図13 暗号化されていないC2データの最終コピーを作成する関数呼び出し
図13 暗号化されていないC2データの最終コピーを作成する関数呼び出し

参照しやすいよう、次の図に、関連する値とその説明とをそれぞれ色をかえて強調表示したバッファを示します。

図14 メモリ内のバイトオフセットとサイズとそれぞれに対応する説明
図14 メモリ内のバイトオフセットとサイズとそれぞれに対応する説明

次にCryptEncrypt関数ラッパーへの呼び出しが行われます。これにより、オフセット0x2F0AD4にあるEAXレジスタへの間接呼び出しを介して実際のAPI関数にアクセスします。

図15 CryptEncrypt関数への関数呼び出しによりC2データを暗号化
図15 CryptEncrypt関数への関数呼び出しによりC2データを暗号化

次の図は、C2データの暗号化前後の状態を示しています。

図16 C2データの暗号化前後の状態
図16 C2データの暗号化前後の状態

C2データを暗号化した後の次のステップはオフセット0x2EFF2CにあるCryptExportKey関数を呼び出して現在の暗号化キーをエクスポートすることです。

図17 CryptExportKeyラッパーへの関数呼び出し
図17 CryptExportKeyラッパーへの関数呼び出し

キーのエクスポート後、オフセット0x2EFF41にあるループ内に、エクスポートされたキー長の0x60バイト分をC2データに書き込むオフセット0x2EFF43の命令があります。

図18 エクスポートされた暗号キーのデータを記入する書き込みループ
図18 エクスポートされた暗号キーのデータを記入する書き込みループ

ここで、CryptDestroyHashへのポインタを含むパラメータつきでAPI関数CryptGetHashParamの呼び出しが行われます。これにより、生成されたハッシュ20バイト分がC2データに書き込まれます。

図19 CryptGetHashParamへの関数呼び出し
図19 CryptGetHashParamへの関数呼び出し

次の図は最終的なC2データがどのようにメモリに保存されるかを示しています。

図20 メモリ内のエクスポートされたキー、ハッシュ値、暗号化されたC2データのバイト
図20 メモリ内のエクスポートされたキー、ハッシュ値、暗号化されたC2データのバイト

C2 へのデータ漏出: HTTP POSTリクエストの生成

この段階で、エクスポートされたキーハッシュ値暗号化されたC2データを含むC2データが完成します。したがって最終段階はデータ漏出を完了させることになります。次の手順で、必要なデータ(IPアドレス、HTTPフォームの構造と値など)を準備します。

図21 データ漏出の前にHTTPの要件の前半部分を満たすために行う関数呼び出し
図21 データ漏出の前にHTTPの要件の前半部分を満たすために行う関数呼び出し

この時点で、後続の関数呼び出しが実行されてHTTPフォームに含まれるバイナリデータが生成されます。次のセクションでは、このような暗号化されたデータとそのC2サーバーへの漏出につながる詳細手順について説明します。

このステップはC2データ(バイト)のWebフォームへのコピーにより構成されています。またこの処理はcopy_c2_dataサブルーチンを実行することで達成しています。この関数は、バイナリ転送に適した入力データを含めたコンテンツタイプが「application/octet-stream」のバイナリMIME添付ファイルを生成します。

図22 バイナリデータをWebフォームにコピーするための関数呼び出し
図22 バイナリデータをWebフォームにコピーするための関数呼び出し

この段階で最終ペイロードはC2サーバーに情報を送信するための環境を準備しています。このために関数呼び出しを実行して必要なデータを取得し、いよいよ最後のHTTPリクエストを実行します。

図23 データ漏出の前にHTTPの要件の後半部分を満たすための関数呼び出し
図23 データ漏出の前にHTTPの要件の後半部分を満たすための関数呼び出し

関数呼び出しのリストで示したように、HttpSendRequestW()API関数を使用してデータをサーバーに送信します。この関数を使用することで送信者はHTTPクライアントが通常送信するデータ量を超えてデータを送信できます。

図24 エクスポートされたキー、ハッシュ値、暗号化されたC2データを含むPOSTリクエストを示すWiresharkのパケットキャプチャ
図24 エクスポートされたキー、ハッシュ値、暗号化されたC2データを含むPOSTリクエストを示すWiresharkのパケットキャプチャ

結論

法執行機関が協調して行ったキャンペーンによって2021年1月下旬にインフラストラクチャをシャットダウンされるまでの間、数年にわたってEmotetには活発なアクティビティが見られました。その攻撃戦術や技術は時間をかけて進化しており、その攻撃チェーンは高度に成熟・洗練されたものでした。結果としてそれが、セキュリティリサーチャーにとっては良いケーススタディとなりました。今回の調査では、C2サーバーのIP選択やデータ暗号化などを含むEmotetのC2通信例を提示することで、Emotetのマルウェアがそうした高度な手法をセキュリティ製品による検出の回避にどのように生かしているのかがもっとわかりやすくなりました。

パロアルトネットワークスのお客様は、次の方法でこの種の攻撃から保護されています。

  1. 脅威防御のシグネチャ212012118521167は、新しいペイロードをダウンロードし、機密情報を送信しようとしているHTTP C2リクエストを特定します。
  2. WildFireCortex XDRはEmotetとそのドロッパを識別してブロックします。

IoC

サンプル
  • 2cb81a1a59df4a4fd222fbcb946db3d653185c2e79cf4d3365b430b1988d485f
ドロッパ
  • bbb9c1b98ec307a5e84095cf491f7475964a698c90b48a9d43490a05b6ba0a79
  • bd1e56637bd0fe213c2c58d6bd4e6e3693416ec2f90ea29f0c68a0b91815d91a
URL
  • http://allcannabismeds[.]com/unraid-map/ZZm6/
  • http://giannaspsychicstudio[.]com/cgi-bin/PP/
  • http://ienglishabc[.]com/cow/JH/
  • http://abrillofurniture[.]com/bph-nclex-wygq4/a7nBfhs/
  • https://etkindedektiflik[.]com/pcie-speed/U/
  • https://vstsample[.]com/wp-includes/7eXeI/
  • http://ezi-pos[.]com/categoryl/x/
IP アドレス
  • 5.2.136[.]90
  • 161.49.84[.]2
  • 70.32.89[.]105
  • 190.247.139[.]101
  • 138.197.99[.]250
  • 152.170.79[.]100
  • 190.55.186[.]229
  • 132.248.38[.]158
  • 110.172.180[.]180
  • 37.46.129[.]215
  • 203.157.152[.]9
  • 157.245.145[.]87