ブログ(脅威調査)

MassLoggerのアンチ解析手法の回避 - 中間者(Man-in-the-Middle)アプローチ

FireEye Front Line Applied Research & Expertise (FLARE) チームは、常に最新の脅威や新たな脅威の最先端を行くように努めています。私は FLARE リバース・エンジニア・チームの一員として、最近、MassLogger として特定されたかなり新しいクレデンシャル・スティーラーの分析依頼を受けました。目新しい機能や機能がないにもかかわらず、このサンプルでは、静的な分析を妨げるために、実行時に共通中間言語 (MSIL) を書き換える洗練された手法が採用されています。この記事を書いている時点では、MassLogger の難読化技術について詳細に論じている論文は 1 つしかありません。そこで私は、この解析結果とMassLogger や同様の手法を用いるマルウェアを分析するためのツールを共有することにしました。ここでは、MassLogger のクレデンシャル・スティーラーと .NET ランタイムについて技術的に深く掘り下げてみましょう。

トリアージ

MassLoggerは資格情報を盗む .NET マルウェアです。これはランチャー (6b975fd7e3eb0d30b6dbe71b8004b06de6bba4d0870e165de4bde7ab82154871) から始まり、気付きさえすれば簡単に迂回可能な単純なアンチデバッグ技術を使用しています。この第一段階のローダーは、第二段階のアセンブリを XOR 復号します。そしてこのアセンブリが Bin-123.exe という名前の MassLogger 最終ペイロード (bc07c3090befb5e94624ca4a49ee88b3265a3d1d288f79588be7bb356a0f9fae) を復号、ロードして実行します。最終ペイロードを簡単に抽出して独立して実行することができるため、ここでは主なアンチ解析手法が使用されているこの最終ペイロードにのみ焦点を当てます。

基本的な静的分析では、あまりエキサイティングなことは明らかになりません。興味深い文字列がいくつか見られますが、マルウェアの機能についてのヒントを与えるには不十分です。解析環境でペイロードを実行すると、サンプルはログファイルを生成します。このログファイルにはマルウェアのファミリー、バージョン、そして重要なことに設定オプションが含まれています。ログファイルのサンプルを図 1 に示します。また、サンプルの実行に伴い、メモリから興味深い文字列を抽出することができます。しかし、基本的な動的分析だけでは、ホスト・インディケータ (HBI)、ネットワーク・インディケータ (NBI)、そしてマルウェアの機能をすべて抽出するには十分ではありません。サンプルとその機能をよりよく理解するためには、より深い分析を行う必要があります。

User Name: user
IP: 127.0.0.1
Location: United States
OS: Microsoft Windows 7 Ultimate 32bit
CPU: Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz
GPU: VMware SVGA 3D
AV: NA
Screen Resolution: 1438x2460
Current Time: 6/17/2020 1:23:30 PM
MassLogger Started: 6/17/2020 1:23:21 PM
Interval: 2 hour
MassLogger Process: C:\Users\user\Desktop\Bin-123.exe
MassLogger Melt: false
MassLogger Exit after delivery: false
As Administrator: False
Processes:
Name:cmd, Title:Administrator: FakeNet-NG - fakenet
Name:iexplore, Title:FakeNet-NG - Internet Explorer
Name:dnSpy-x86, Title:dnSpy v6.0.5 (32-bit)
Name:cmd, Title:Administrator: C:\Windows\System32\cmd.exe
Name:ProcessHacker, Title:Process Hacker [WIN-R23GG4KO4SD\user]+ (Administrator)

### WD Exclusion ###
Disabled

### USB Spread ###
Disabled

### Binder ###
Disabled

### Window Searcher ###
Disabled

### Downloader ###
Disabled

### Bot Killer ###
Disabled

### Search And Upload ###
Disabled

### Telegram Desktop ###
Not Installed

### Pidgin ###
Not Installed

### FileZilla ###
Not Installed

### Discord Tokken ###
Not Installed

### NordVPN ###
Not Installed

### Outlook ###
Not Installed

### FoxMail ###
Not Installed

### Thunderbird ###
Not Installed

### QQ Browser ###
Not Installed

### FireFox ###
Not Installed

### Chromium Recovery ###
Not Installed

### Keylogger And Clipboard ###

 

[20/06/17]  [Welcome to Chrome - Google Chrome]
[ESC]

[20/06/17]  [Clipboard]
Vewgbprxvhvjktmyxofjvpzgazqszaoo

図 1: MassLogger ログのサンプル

単純な逆コンパイル

他の多くの .NET マルウェアと同様に、MassLogger はすべてのメソッド名やメソッド制御フローさえも難読化します。de4dot を使用すると MassLogger の難読化ペイロードを自動的に解読することができます。しかし、解読されたペイロードを見ると、すぐに大きな問題が見つかります。図 2 に示すように、ほとんどのメソッドにはほとんどロジックが含まれていません。


図2: 空のメソッドを示す dnSpy

dnSpyの中間言語 (IL)ビューのオリジナルの MassLogger ペイロードを見ると、ほとんどのメソッドは何のロジックも含まれておらず、何の結果も返さないことが確認できます。これは明らかに本物のマルウェアではありません。サンプルが実際に悪意のあるアクティビティを実行し、ログファイルにログを記録していることが動的解析ですでに観察されているからです。いくつかのメソッドが残っていますが、最も注目すべきは、メイン・モジュールのコンストラクタで最初に呼び出された、トークン 0x0600049D を持つメソッドです。


図3: メソッドの詳細を示す dnSpy の IL ビュー

メソッド0x0600049Dの制御フローは、一連の switch 文を用いて難読化されています。デバッガとしての dnSpy の助けを借りて、このメソッドのおおまかなロジックをある程度追いかけることができます。しかし、このメソッドを完全に分析するのは非常に時間がかかります。その代わり、最初にこのペイロードを分析するとき、私はヒントを探すためにモジュール全体を素早くスキャンすることにしました。幸いにも、基本的な静的解析の間に見落としていたいくつかの興味深い文字列:図4で示しているような clrjit.dll, VirtualAlloc, VirtualProtect, WriteProcessMemory を見つけました。


図4: モジュール内に散在する興味深い文字列

簡単にインターネットで "clrjit.dll "と "VirtualProtect" を検索すると、Just-In-Time Hooking と呼ばれる手法を説明しているいくつかの記事が出てきます。本質的には、JIT Hooking は、JIT コンパイラが MSIL をアセンブリ(x86、x64 など)にコンパイルしようとしているcompileMethod() 関数にフックをインストールする手法です。フックが設置されていると、マルウェアは各メソッドボディを、元のマルウェアのロジックを含む本物の MSIL に簡単に置き換えることができます。このプロセスを完全に理解するために、.NET 実行ファイル、.NET メソッド、そして MSIL がどのように x86 または x64 アセンブリに変わるのかを探ってみましょう。

.NET実行ファイルのメソッド

NET 実行ファイルは、Portable Executable (PE) 形式のバイナリです。PE ファイル形式.NET メタデータ、.NET トークンテーブルについて詳しく説明しているリソースはたくさんあります。読者の皆さんには、先に進む前に少し寄り道をして、これらのトピックについて記憶を新たにしておくことをお勧めします。この記事では、これ以上の詳細には触れませんが、代わりに .NET メソッドに焦点を当てていきます。

.NET アセンブリの各 .NET メソッドは、トークンによって識別されます。メソッドだけでなく、モジュール、クラス、メソッド プロトタイプ、文字列など、.NET アセンブリ内のすべてのものはトークンによって識別されます。図 5 に示すトークン 0x0600049D のメソッドを見てみましょう。最上位バイト (0x06) は、このトークンがメソッド・トークン (タイプ 0x06) であることを示しており、その他のトークン、例えばモジュール・トークン (タイプ 0x00)、TypeDef トークン (タイプ 0x02)、または LocalVarSig トークン (タイプ 0x11) ではないことを示しています。最下位の 3 バイトはメソッドの ID を示し、この場合は 0x49D (10 進数で 1181) です。この ID は、メソッド ID (MID) またはメソッドの行 ID とも呼ばれます。


図 5: メソッド 0x0600049D のメソッド詳細

このメソッドの詳細を調べるために、図 6 に示すように、.NET メタデータ・ディレクトリから .NET メタデータ・ストリーム、そして "#~" ストリームのテーブルを探します。Method テーブルのエントリ番号 1181 または 0x49D をたどるとメソッドのメタデータが確認でき、メソッドボディの相対仮想アドレス (RVA)、各種フラグ、メソッド名へのポインタ、メソッドシグネチャへのポインタ、そして最後に、このメソッドのパラメータ仕様へのポインタが含まれています。MIDは0ではなく1から始まることに注意してください。


図 6: PE ファイルヘッダからのメソッドの詳細

メソッド 0x0600049D の場合、メソッドボディの RVA は 0xB690 です。この RVA は、RVA が 0x2000 である .text セクションに属します。したがって、このメソッドボディは .text セクションの 0x9690 (0xB690 - 0x2000) バイトから始まります。.textセクションは、セクションヘッダに従ってファイル内の0x200バイトから始まります。その結果、ファイル内では 0x9890 (0x9690 + 0x200) バイトオフセット箇所でメソッドボディを見つけることができます。図 7 にメソッドボディを示します。


図 7: 16 進数エディタでのメソッド 0x0600049D のボディ

.NETメソッドボディ

.NET メソッドボディは、メソッドボディヘッダから始まり、MSIL バイトが続きます。.NET メソッドには、tiny メソッドと fat メソッドの 2 種類があります。メソッドボディヘッダの最初のバイトを見ると、そのメソッドがtiny (最後の 2 ビットが 10) か fat (最後の 2 ビットが 11) であるかどうかがわかります。

.NET Tiny メソッド

メソッド 0x06000495 を見てみましょう。先に説明したのと同じ手順で、メソッド・テーブルの行番号 0x495 (10進数で1173) をチェックして、メソッドボディの RVA が 0x7A7C であることを確認、そしてファイルへのオフセットとして 0x5C7C に変換します。このオフセットでは、メソッドボディの最初のバイトは 0x0A (2 進数では 0000 1010) です。


図 8: メソッド 0x06000495 メタデータとボディ

下位 2 ビットが 10 であることから、0x06000495 は tiny メソッドであることがわかります。tiny メソッドの場合、メソッドボディヘッダは 1 バイトの長さです。最下位の 2 つのビットは、これが tiny メソッドであることを示す 10 であり、最上位の 6 つのビットは、後に続く MSIL のサイズ (すなわち MSIL の長さ) を示しています。この場合、最も重要な 6 ビット000010 であり、メソッドボディが 2 バイトの長さであることを示しています。0x06000495のメソッドボディ全体は、0A 16 2Aで、その後にNULLバイトが続きます。dnSpy による逆アセンブル結果を図9に示します。


図9: dnSpy ILビューのメソッド0x06000495

.NET Fat メソッド

ファイルのオフセット 0x9890 (RVA 0xB690) にあるメソッド 0x0600049D (エントリ番号 1181) に戻ると、メソッドボディの最初のバイトは 0x1B (バイナリでは 0001 1011) です。下位2ビットは 11 であり、0x0600049D が fat メソッドであることを示しています。 fat メソッドのヘッダは12バイトの長さです。その構造はこのブログ記事の範囲を超えるため割愛しますが、我々が本当に気になるフィールドは、この fat ヘッダのオフセット 0x04 バイトの箇所にある 4 バイト長のフィールドです。このフィールドは、このメソッドボディヘッダに続く MSIL の長さを指定します。メソッド 0x0600049D の場合、メソッドボディヘッダ全体は「1B 30 08 00 A8 61 00 00 75 00 00 11」であり、続くMSILの長さは「A8 61 00 00」または0x61A8 (10進数で25000) バイトとなります。


図 10: 16 進数エディタでのメソッド 0x0600049D のボディ

JITコンパイル

メソッドが tiny であっても fat であっても、そのままでは実行されません。.NET ランタイムは、メソッドを実行する必要がある場合、前述のプロセスに従って、メソッドボディヘッダと MSIL バイトを含むメソッドボディを見つけます。メソッドを初めて実行する必要がある場合、.NET ランタイムは、MSIL バイトを受け取り、現在のプロセスが 32 ビットか 64 ビットかに応じて、x86 または x64 アセンブリにコンパイルする JIT コンパイラを呼び出します。準備が終わると、JIT コンパイラは最終的に compileMethod() 関数を呼び出します。.NETランタイムプロジェクト全体はオープンソースで、GitHub で公開されています。compileMethod() 関数が以下のようなプロトタイプを持っていることが簡単にわかります (図 11)。

CorJitResult __stdcall compileMethod (
    ICorJitInfo                       *comp,               /* IN */
    CORINFO_METHOD_INFO               *info,               /* IN */
    unsigned /* code:CorJitFlag */    flags,               /* IN */
    BYTE                              **nativeEntry,       /* OUT */
    ULONG                             *nativeSizeOfCode    /* OUT */
);

図 11: compileMethod() 関数プロトタイプ

図 12 は、CORINFO_METHOD_INFO 構造体を示しています。

struct CORINFO_METHOD_INFO
{
      CORINFO_METHOD_HANDLE       ftn;
      CORINFO_MODULE_HANDLE       scope;
      BYTE *                      ILCode;
      unsigned                    ILCodeSize;
      unsigned                    maxStack;
      unsigned                    EHcount;
      CorInfoOptions              options;
      CorInfoRegionKind           regionKind;
      CORINFO_SIG_INFO            args;
      CORINFO_SIG_INFO            locals;
};

図 12: CORINFO_METHOD_INFO 構造体

ILCode はコンパイルするメソッドの MSIL へのポインタであり、ILCodeSize は MSIL の長さを教えてくれます。compileMethod() の戻り値は成功か失敗かを示すエラーコードです。成功の場合、nativeEntry ポインタには、MSIL からコンパイルされた x86 命令または x64 命令を含む実行可能メモリ領域のアドレスが格納されます。

MassLogger の JIT Hooking

MassLogger の話に戻りましょう。メインモジュールは初期化が実行されるとすぐに、まず他のメソッドの MSIL を解読します。その後、独自バージョンの compileMethod() (メソッド 0x06000499) を実行するためのフックをインストールします。このメソッドは、元の compileMethod() に渡された info 引数の ILCode と ILCodeSize フィールドを、実際のマルウェアの MSIL バイトで置き換えます。

MSIL バイトを置き換えるだけでなく、MassLogger はモジュールの初期化時にメソッドボディヘッダをパッチします。図 13 に示すように、ディスク上のメソッド 0x060003DD のメソッドボディヘッダ (ファイルオフセット 0x3CE0) は、メモリ上のヘッダ (RVA 0x5AE0) とは異なります。完全に一致しているのは、メソッドが tiny か fat かを示す最下位の 2 つのビットだけです。このアンチ解析手法をうまく打ち破るためには、正しいメソッドボディヘッダと同様に、実際の MSIL のバイト列を回復しなければなりません。


図 13: ディスク上でのレスト時とメモリにロードされた時のヘッダが異なる同じメソッドボディ

JITMを用いてメソッドボディの書き換えに対処する

MSIL とメソッドボディヘッダを自動的に復元するために、FLARE チームの別のメンバーが提案してくれたアプローチの 1 つは、MassLogger モジュールのコンストラクタをロードして実行する前に compileMethod() 関数に独自のフックをインストールすることです。compileMethod() をフックする方法については、複数チュートリアルオープンソースプロジェクトがあり、マネージドフック (新しい compileMethod() は C# で書かれたマネージドメソッド) とネイティブフック (新しい compileMethod() はネイティブ C または C++ で記述) の手法が存在します。しかし、MassLogger の compileMethod() のフックの仕方が独特であるため、前述の多くのプロジェクトで使用されている vtable フックの手法を使うことができません。そのため、ここで新しいプロジェクト JITM を紹介したいと思います。これは PolyHook ライブラリで実装されているインラインフックを使用します。JITM には compileMethod() のラッパーが付属しており、オリジナルの compileMethod() を呼び出す前に、メソッドボディヘッダと MSIL バイトを JSON ファイルに記録します。

フックに加えて、JITM には .NET ローダも含まれています。このローダーは最初にネイティブフックDLL (jitmhook.dll) をロードし、フックをインストールします。次に、ローダは MassLogger のペイロードをロードし、そのエントリポイントを実行します。これにより、MassLogger のモジュール初期化コードが実行され、そのフックがインストールされますが、元の compileMethod() の代わりに jitmhook.dll のコードがフックされます。MassLogger のエントリポイントを実行するための代替アプローチは、RuntimeHelpers.PrepareMethod() API を呼び出して JIT コンパイラをすべてのメソッドで実行させることです。このアプローチの方が、マルウェアの実行を回避しつつ、サンプルの通常のコードパスでは呼び出されないメソッドも復元できる可能性があるため、より良い方法です。しかし、すべてのメソッドを適切にコンパイルするためには、追加の作業が必要です。

MassLogger メソッドをロードして復元するには、以下のコマンドを実行します (図 14)。

jitm.exe Bin-123.exe [optional_timeout]

図 14: jitm を実行するコマンド

タイムアウトに達すると、カレントディレクトリに jitm.logjitm.json というファイルが作成されているのがわかるはずです。jitm.json には Bin-123.exe から復元されたメソッドトークン、メソッドボディヘッダと MSIL が記録されています。あとは.NETのメタデータを再構築して静的解析を行うだけです。


図 15: jitm.json のサンプル

アセンブリの再構築

復号化されたメソッドボディヘッダと MSIL は、元の .NET アセンブリには適切に収まらないかもしれません。もっとも簡単な手法は MassLogger に新しいセクションとセクションヘッダを追加する方法です。PE セクションヘッダとデータを追加する方法については多くリソースがありますが、自動化となると簡単にできるものは見当たらなかったため、JITM ではこのプロセスを自動化するための Python 2.7 ヘルパースクリプト、Scripts\addsection.py を用意しました。

図 16 で示されているように、各メソッドのメソッドボディヘッダと MSIL を新しい PE セクションに追加しておけば、.NET のメタデータを簡単に解析して、各メソッドの RVA が新しいセクション内の正しいメソッドボディを指すように修正することができます。残念ながら、.NET のメタデータと MethodDef テーブルを解析する手軽な Python ライブラリは見当たりませんでした。そのため、 JITM には部分的な .NET メタデータパーサー、Script\pydnet.py も含まれています。このスクリプトは pefile vivisect モジュールを使用して、PE ファイルを Method テーブルまで解析し、すべてのメソッドと関連する RVA を抽出します。


図16:FLARE という名前の追加セクションの追加前と追加後の Bin-123.exe

最後に、JITM では以下のタスクを実行するための Script\fix_assembly.py を提供しています。

  1. jitm.json で回収した各メソッドのメソッドボディヘッダと MSIL を section.bin という名前の一時的なバイナリファイルに書き込み、同時に関連付けられたメソッドトークンと section.bin へのオフセットを保持しておく
  2. addsection.py を使用して、section.binBin-123.exe に追加し、データを Bin-123.fixed.exe などの新しいファイルに保存する
  3. pydnet.py を使用して Bin-123.fixed.exe を解析し、MethodDef テーブルの各メソッドエントリの RVA フィールドを更新して、新しいセクションに正しい RVA を指すようにする

最終的な結果は、部分的に再構築された .NET アセンブリです。このアセンブリを正しく実行するためには追加の作業が必要ですが、静的解析を実行してマルウェアの機能をある程度理解するには十分です。

マルウェア構成の復号化ロジックを実装した再構築されたメソッド 0x0600043E を見てみましょう。元の MSIL と比較して、再構築された MSIL では、マルウェアが PKCS7 パディングを含む CBC モードで AES-256 を使用していることがわかります。動的解析と静的解析を組み合わせることで、キーが「Vewgbprxvhvjktmyxofjvpzgazqszaoo」であることと、IV が引数として渡された Base64 エンコードされたバッファの一部であることを容易に特定することができます。


図 17: アセンブリ修正前と修正後のメソッド 0x0600043

この知識を武器に、マルウェアの構成を解読し、すべての HBI と NBI を取得するための簡単なツールを書くことができます (図 18)。

                              BinderBytes: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                BinderName: Mzvmy_Nyrrd
                BinderOnce: false
        DownloaderFilename: Hrebxs
            DownloaderOnce: false
             DownloaderUrl: Vrwus
              EmailAddress: [email protected]
               EmailClient: smtp.outlook.com
               EmailEnable: true
                 EmailPass: services000
                 EmailPort: 587
               EmailSendTo: [email protected]
                  EmailSsl: True
        EnableAntiDebugger: false
        EnableAntiHoneypot: false
       EnableAntiSandboxie: false
          EnableAntiVMware: false
              EnableBinder: false
           EnableBotKiller: false                               
     EnableBrowserRecovery: true
EnableDeleteZoneIdentifier: false
          EnableDownloader: false
            EnableForceUac: false
             EnableInstall: false
           EnableKeylogger: true
          EnableMemoryScan: false
               EnableMutex: false
          EnableScreenshot: false
     EnableSearchAndUpload: false
           EnableSpreadUsb: false
         EnableWDExclusion: false
      EnableWindowSearcher: false
             ExectionDelay: 6
         ExitAfterDelivery: false
                 FtpEnable: false
                   FtpHost: ftp://127.0.0.1
                   FtpPass:
                   FtpPort: 21
                   FtpUser: Foo
               InstallFile: Pkkbdphw
             InstallFolder: %AppData%
       InstallSecondFolder: Eqrzwmf
                       Key:
                     Mutex: Ysjqh
               PanelEnable: false
                 PanelHost: http://example.com/panel/upload.php
 SearchAndUploadExtensions: .jpeg, .txt, .docx, .doc,
  SearchAndUploadSizeLimit: 500000
    SearchAndUploadZipSize: 5000000
              SelfDestruct: false
           SendingInterval: 2
                   Version: MassLogger v1.3.4.0
    WindowSearcherKeywords: youtube, facebook, amazon,

図 18: 復号された設定

結論

JIT コンパイラのフックを使用して MSIL を置き換えることは、静的分析をほぼ不可能にする強力な手法です。この手法は新しいものではありませんが、ConfuserEx のような広く利用可能なプロテクタを使用する代わりに独自の実装を適用しようとする .NET マルウェアは、言うまでもなく、多くありません。本ブログ記事と JITM によって、アナリストが MassLogger や同様の手法を使用する将来の亜種を打ち負かすためのツールと知識を手に入れることができるようになることを願います。

このような仕事に興味があり、マルウェア解析やリバース・エンジニアリングの分野で最先端を行くことに喜びを感じているのであれば、FLARE (Front Line Applied Research and Expertise) チームはあなたにぴったりの職場かもしれません。FLARE チームは日々楽しく刺激的な課題に直面しており、これらの課題に真正面から取り組むチームメンバーを常に募集しています。FireEye のキャリアページをご覧になり、あなたに合った求人があるかどうかを確認してください。

コントリビューター (アルファベット順)

  • Tyler Dean (@spresec): Technical review of the post
  • Michael Durakovich: Technical review of the post
  • Stephen Eckels (@stevemk14ebr): Help with porting JITM to use PolyHook
  • Jon Erickson (@evil-e): Technical review of the post
  • Moritz Raabe (@m_r_tz): Technical review of the post

 

本ブログは、米FireEyeが公開した August 06, 2020「Bypassing MassLogger Anti-Analysis — a Man-in-the-Middle Approach」(英語)の日本語抄訳版です。

日本語版:Reviewed by Takahiro Sugiyama