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実行可能ファイルを想定しています。参照しやすいように関数オフセット、名前、カスタム名を次の図に記載しておきます。
注: 使用されたサブルーチンはここに提示した関数オフセットから簡単に見つかるのでこの図には記載していません。
今回の分析は、エントリポイントとなる関数c2_logic_ep(sub_2E2C63)から見ていきます。
暗号化API関数
このマルウェアは、2つの主要関数encryption_functions_oneとencryption_functions_twoを使用します。どちらの関数もMicrosoftのBase Cryptography(CryptoAPI)を使っています。以下、マルウェア実行中にこれらの暗号化API関数が使用するプロパティと実行するアクションを記載します。
- CryptAcquireContextW: PROV_DH_SCHANNELをプロバイダタイプ(0x18)として使います。CRYPT_VERIFYCONTEXTフラグとCRYPT_SILENTフラグとのビット単位OR演算(0xf0000040)を行ってユーザーインターフェイス(UI)がユーザーに表示されないようにします。
- CryptDecodeObjectEx: メッセージエンコードタイプX509_ASN_ENCODINGとPKCS_7_ASN_ENCODINGとをビット単位OR演算(0x10001)したもの、X509_BASIC_CONSTRAINTS(0x13)の構造体タイプ、デコードされる合計0x6a分のバイトを使います。
- CryptImportKey: CUR_BLOB_VERSION(0x2) バージョンでサイズ0x74バイト、タイプPUBLICKEYBLOB(0x6) のキーブロブをインポートします。
- CryptGenKey: 値にCALG_AES_128(0x0000660e)を設定したALG_IDを使用して128ビットのAESセッションキーを生成します。
- CryptCreateHash: 値にCALG_SHA(0x00008004)を設定したALG_IDを使用してその名の通りSHAハッシュアルゴリズムを設定します。
- CryptDuplicateHash: 複製されるハッシュへのハンドルを受け取ります。
- CryptEncrypt: この関数はCryptGenKey関数によって生成された暗号化キーへのハンドルと、CryptCreateHashによって生成されたハッシュオブジェクトへのハンドルという2つの主要パラメータを受け取ります。この値が、暗号化後のCryptEncrypt関数呼び出しと、パラメータとしてのC2データへのポインタ渡しに使用されます。
- CryptExportKey: SIMPLEBLOB(0x1)タイプとCRYPT_OAEP(0x00000040)をフラグとして使用します。キーブロブがエクスポートされるバッファへのポインタは、マルウェアのC2データの一部です。
- CryptGetHashParam: CryptExportKey関数同様、宛先ポインタはマルウェアのC2データの一部です。
- CryptDestroyHash: その名が示す通り指定されたハッシュを破棄します。
コンピューター IDの生成と長さのチェック
generate_machine_id関数は、その名が示す通り、感染端末のコンピューター ID生成をになっています。コンピューター IDの生成には_snprintf関数の呼び出しが使われます。この関数はフォーマット文字列%s_%08Xを使ってGetComputerNameAとGetVolumeInformationWが生成した値を連結します。今回の分析で使用した検証マシンの特定ケースでは、この結果の値はANANDAXPC_58F2C41Bとなりました。
コンピューター IDが生成されると長さチェック検証も生成されます。これは「lstrlen」関数のラッパーであるgen_machine_id_lengthの呼び出しと、前の関数呼び出しからの戻り値をパラメーターとして渡すことで実現されています。私たちの検証マシンのケースでは、この結果の長さは「12」となりました。これらの値はC2データの一部として使用されることから特定スタック変数内に存在します。続いてwrite_GoR関数に対する新たな関数呼び出しが行われます。ただし、私たちの分析と戻り値(0x16F87C)の使われ方からは、これが何を目的とするものなのかはわかりませんでした。C2データの最後にあることから区切り文字ではないかと考えられます。
オペレーティングシステムのデータ収集
漏出されるデータにはオペレーティングシステム情報も含まれています。この漏出にはcollect_os_data関数呼び出しが使われていました。
この関数はRtlGetVersionを呼び出し、OSVERSIONINFOW構造体にデータを保存します。GetNativeSystemInfoもSYSTEM_INFO構造体内にデータを保存して同じことを行います。
これらデータ構造体への入力が済むと、0x2EC3DB(戻り値)、0x2EC440(MajorVersion)、0x2EC3DB、0x2EC3D0(MinorVersion)、0x2EC45A(Architecture|PROCESSOR_ARCHITECTURE_INTEL)の各オフセット位置の命令により特定のデータがフェッチされます。
戻り値は、MajorVersion、MinorVersion、Architecture、そしてNT_PRODUCT_TYPE列挙データ型のシンボリック定数(NtProductWinNT)であるRtlGetNtProductType呼び出しの戻り値(0x1)、これら固定値との加算と乗算で計算されます。次のPythonコードは、それらの値を生成するロジックをシミュレートしたものです。
リモートデスクトップサービスセッション情報の収集
現在のプロセスのプロセスIDを取得するGetCurrentProcessIdの呼び出しを含むさらに多くの呼び出しが実行され、その戻り値がProcessIdToSessionId関数にパラメータとして渡されます。MSDNの説明によるとProcessIdToSessionId関数は「指定したプロセスに関連付けられたリモートデスクトップサービスセッションを取得する」とあります。この関数の戻り値は、現在のプロセスが実行されているターミナルサービスセッションを示します。
プロセススキャンとC2データ収集
この関数はCreateToolhelp32Snapshot、Process32FirstW、GetCurrentProcessId、Process32NextWの各関数を呼び出す従来型の方法を実行することで、システム上で実行されているアクティブなプロセスを収集します。この関数に入る前に、オフセット0x2E4715にある命令がローカル変数のアドレスをEAXレジスタにロードしてスタックにプッシュします。この変数には最終的にプロセスデータ情報を受け取ることになるRtAllocateHeap関数の呼び出しにより生成したポインタが含まれています。
この関数はcopy_collected_data_parentという名前のサブルーチンも呼び出します。実行中、この関数はRtlAllocateHeap関数呼び出しとそれに続く複数のmemcpyラッパー関数呼び出しにより収集済みC2データを新しく割り当てられたセクションにコピーすることで、新しいメモリセクションを生成します。
次に呼び出す関数がHTTP_LAUNCHERで、これには他のタスクの中でも特にWeb機能を提供するサブルーチンが含まれています。この時点で一連の変数はそれまでに実行された関数からの対応する戻り値で初期化されています。次のASCIIダンプは、変数アドレス、関連データ、そして「どの関数ないし命令オフセットが特定のデータを提供したか」に関する情報を示しています。
次のステップはwrite_collected_dataサブルーチンを呼び出すc2_data_write関数で、これはパラメータとして次の2つの値を渡します。
- C2データ(0x2EAC3E)へのポインタ
- オフセット0x2F989BにあるRtlAllocateHeap関数の呼び出しによって生成された、新しいメモリ割り当ての戻り値(アドレス)
この新しく生成されたデータはあるアルゴリズムを通過します。このアルゴリズムでは、書き込み(オフセット0x2FA830)に加え、C2データの特定のバイト(オフセット0x2FA6DE)、特に一部のファイル名拡張子の変更も行います。
データが収集されるとwrite_c2_data_zeroへの呼び出しが行われ、これがAllocateHeap (0x2E99DC)関数を呼び出して追加のメモリが割り当てられます。この関数は最終的に2回呼び出され、さらにサブルーチンを呼び出します。それらのサブルーチンでは、write_c2_data_one関数のオフセット0x2F362Aにある命令が2つのDWORD値(固定値の0x1、C2データ長の0x132)を生成します。次のステップはcopy_c2_data(オフセット0x2F794Cにあるmemcpyへのラッパー)関数への呼び出しです。これは、C2データを前述の2つの値の隣の新しい場所にコピーします。
次のシーケンシャルな関数実行は、CryptDuplicateHashへの呼び出しです。この後、copy_binary_dataへの呼び出しが行われ、それによって最終的なC2データが新しいメモリ割り当てにコピーされます。この場所にはこのあとの後続のステップでCryptEncrypt関数が暗号化する前の最後のC2データが含まれています。
参照しやすいよう、次の図に、関連する値とその説明とをそれぞれ色をかえて強調表示したバッファを示します。
次にCryptEncrypt関数ラッパーへの呼び出しが行われます。これにより、オフセット0x2F0AD4にあるEAXレジスタへの間接呼び出しを介して実際のAPI関数にアクセスします。
次の図は、C2データの暗号化前後の状態を示しています。
C2データを暗号化した後の次のステップはオフセット0x2EFF2CにあるCryptExportKey関数を呼び出して現在の暗号化キーをエクスポートすることです。
キーのエクスポート後、オフセット0x2EFF41にあるループ内に、エクスポートされたキー長の0x60バイト分をC2データに書き込むオフセット0x2EFF43の命令があります。
ここで、CryptDestroyHashへのポインタを含むパラメータつきでAPI関数CryptGetHashParamの呼び出しが行われます。これにより、生成されたハッシュ20バイト分がC2データに書き込まれます。
次の図は最終的なC2データがどのようにメモリに保存されるかを示しています。
C2 へのデータ漏出: HTTP POSTリクエストの生成
この段階で、エクスポートされたキー、ハッシュ値、暗号化されたC2データを含むC2データが完成します。したがって最終段階はデータ漏出を完了させることになります。次の手順で、必要なデータ(IPアドレス、HTTPフォームの構造と値など)を準備します。
この時点で、後続の関数呼び出しが実行されてHTTPフォームに含まれるバイナリデータが生成されます。次のセクションでは、このような暗号化されたデータとそのC2サーバーへの漏出につながる詳細手順について説明します。
このステップはC2データ(バイト)のWebフォームへのコピーにより構成されています。またこの処理はcopy_c2_dataサブルーチンを実行することで達成しています。この関数は、バイナリ転送に適した入力データを含めたコンテンツタイプが「application/octet-stream」のバイナリMIME添付ファイルを生成します。
この段階で最終ペイロードはC2サーバーに情報を送信するための環境を準備しています。このために関数呼び出しを実行して必要なデータを取得し、いよいよ最後のHTTPリクエストを実行します。
関数呼び出しのリストで示したように、HttpSendRequestW()API関数を使用してデータをサーバーに送信します。この関数を使用することで送信者はHTTPクライアントが通常送信するデータ量を超えてデータを送信できます。
結論
法執行機関が協調して行ったキャンペーンによって2021年1月下旬にインフラストラクチャをシャットダウンされるまでの間、数年にわたってEmotetには活発なアクティビティが見られました。その攻撃戦術や技術は時間をかけて進化しており、その攻撃チェーンは高度に成熟・洗練されたものでした。結果としてそれが、セキュリティリサーチャーにとっては良いケーススタディとなりました。今回の調査では、C2サーバーのIP選択やデータ暗号化などを含むEmotetのC2通信例を提示することで、Emotetのマルウェアがそうした高度な手法をセキュリティ製品による検出の回避にどのように生かしているのかがもっとわかりやすくなりました。
パロアルトネットワークスのお客様は、次の方法でこの種の攻撃から保護されています。
- 脅威防御のシグネチャ21201、21185、21167は、新しいペイロードをダウンロードし、機密情報を送信しようとしているHTTP C2リクエストを特定します。
- WildFireとCortex 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