Windowsプロセス内で実行されるPEヘッダーのないマルウェアを詳細分析

文●フォーティネットジャパン 編集●ASCII

提供: フォーティネットジャパン

  • この記事をはてなブックマークに追加
  • 本文印刷

本記事はフォーティネットジャパンが提供する「FORTINETブログ」に掲載された「ダンプされたPEヘッダーのないマルウェアの詳細分析」を再編集したものです。

バックグラウンド

 この分析は、FortiGuardインシデントレスポンスチームが実施したインシデント調査の一環として行われたものです。

 分析により、侵害されたマシンで数週間にわたって実行されていたマルウェアが発見されました。脅威アクターは一連のスクリプトとPowerShellを実行することで、Windowsプロセス内でマルウェアを実行していました。このマルウェアの元の実行ファイルは入手できませんでしたが、実行中のマルウェアプロセスのメモリダンプと侵害されたマシンの完全なメモリダンプ(33GBのfulloutファイル)の取得には成功しました。

図1:侵害されたマシンの完全なメモリダンプファイル

 図1は、ダンプしたメモリファイルfulloutの詳細なファイル情報を示しています。マルウェアを分析するために、このファイルをスキャンしてローカルテスト環境を構築しました。

ダンプされたマルウェアファイル

 このマルウェアは、dllhost.exeプロセス(PID 8200)内で実行されていました。ダンプされたファイルの名前は、pid.8200.vad.0x1c3eefb0000-0x1c3ef029fff.dmpです。ファイル名により、マルウェアがメモリに読み込まれてアドレス範囲0x1c3eefb0000~0x1c3ef029fffに展開されたことがわかります。

図2:ダンプされたマルウェアファイル

 ダンプされたファイルは、展開された64ビットPE(Portable Executable)ファイルです。Windowsローダーは実行時に、PEファイルを読み込んで展開するために、ファイルのDOSヘッダーとPEヘッダーを解析します。展開が完了すると、これらのヘッダーは不要になります。マルウェアが分析のためにファイルにダンプされるのを回避するために、一部のマルウェアではこれらのヘッダー領域がゼロ(このマルウェアの場合)やランダムデータで上書きされて破壊されています。図2のように、DOSヘッダーとPEヘッダーが両方とも破損しているため、メモリから実行ファイル全体を再構築することが困難になっています。

ダンプしたマルウェアをローカルで展開

 マルウェアを動的に分析するためには、侵害されたシステムの環境をローカルで複製する必要がありました。そのために、dllhost.exeプロセスをデバッガー内で起動して、ダンプしたマルウェアの展開先となるターゲットプロセスとして使用する必要がありました。これにより、マルウェアをローカル分析環境内で分析できるようになります。

 この制御された設定でマルウェアを適切に実行できるようにする準備には、いくつかの複雑な手順が含まれます。

・エントリポイントを特定する

 最初のステップは、エントリポイント関数(スタート関数)の特定です。エントリポイント関数は、マルウェアがWindowsローダーによってメモリに読み込まれたときに実行される初期コードです。

 エントリポイント関数のオフセットは、通常はPEヘッダーに保存されていますが、このマルウェアでは違います。当チームは、手動でエントリポイント(スタート関数)を特定する必要がありました。これまでの経験では、エントリポイント関数の最初の命令は、通常は“sub rsp, 28h”としてコンパイルされていますが、この命令は他の関数にも含まれていることがあります。しかし、マルウェアをIDA Proにダンプすることで、この命令のすべての出現箇所をIDA Proのデータベースで探し出すことができました。

 幸いなことに、マルウェアかもしれない命令の事例は8件だけでした(図3)。分析により、4番目の関数(0x1C3EEFEE0A8)がエントリポイントであることが判明しました。

図3:エントリポイント関数の特定

・主要なメモリを割り当てる
 
 新たに起動されたdllhost.exeプロセス内で、ダンプしたマルウェアを展開するために、私たちは図4に示すようにいくつかの命令を手動で実行しました。侵害されたシステム内と同じベースアドレス(0x1C3EEB70000)で、関連するVirtualAlloc() APIが呼び出されます。

図4:dllhost.exe内で新たに割り当てられたメモリ

 割り当てが完了すると、ダンプしたマルウェアが新たに作成されたメモリにコピーされました。

・インポートテーブルを解決する

 PEファイルのインポートテーブルには、そのファイルが依存しているWindows APIがリストされています。これらのAPI読み込みアドレスは、他のWindowsシステムでは異なります。ダンプしたマルウェアをローカルシステムで実行して分析するには、これらのアドレスをローカルシステムに読み込まれたアドレスに変更する必要がありました。

図5:マルウェアのインポートテーブルの一部

 図5は、インポートテーブルにリストされていたWindows APIのアドレスの一部です。分析に基づき、この情報から最終的なAPIアドレスを算出できます。

 例えば、0x1C3EF0240D0のAPIアドレスは、図5に示すように0x1C3EEEE1CE0になります。以下のASMコードをアドレス0x1C3EEEE1CE0hで実行することで、APIアドレスが 0x7FFD74224630として算出されます。

001C3EEEE1CE0   mov r10, 0E528F49552F112B4h
001C3EEEE1CEA   mov r11, 0E5288B6826D35484h
001C3EEEE1CF4   xor r11, r10
001C3EEEE1CF7   jmp r11   ; 0x7FFD74224630


 私たちはVolatilityツールを使用して、fulloutファイルから読み込まれたdllhost.exeプロセス(PID 8200)内のモジュールをリストしました。図6を見てわかるように、0x7FFD74224630のAPIはモジュールGDI32.dllからエクスポートされます。

図6:dllhost.exeに読み込まれたモジュールのリスト

 fulloutファイルからGDI32.dllをダンプして分析することで、アドレス0x7FFD74224630のAPIが、侵害されたシステム内のGetObjectW()に対応していることがわかりました。

 分析に使用したローカルテスト環境では、この同じAPIがアドレス0x07FFFF77CB870に位置しています。このAPIの場合、元のアドレスがローカルアドレスで置き換えられています。
このマルウェアは257のWindows APIを使用しており、以下の16のモジュールにわたるアドレス変更が必要です。

・kernel32.dll
・ws2_32.dll
・ntdll.dll
・gdi32.dll
・shlwapi.dll
・sspicli.dll
・user32.dll
・shell32.dll
・msvcrt.dll
・advapi32.dll, 
・comctl32.dll
・crypt32.dll
・gdiplus.dll
・ole32.dll
・rpcrt4.dll 
・userenv.dll

 インポートテーブル内の各APIについて、GetObjectW() APIの場合と同じ方法で、元のアドレスを対応するローカルアドレスで置き換える必要があります。

 さらに、dllhost.exeが自動的に読み込まない必要なモジュールをすべて読み込む必要がありました。そのためには、モジュール名を指定してLoadLibraryA()またはLoadLibraryW() APIを呼び出して、それらのモジュールをマルウェアのメモリに読み込む必要があります。

 図7は、デバッガー内で実行されているマルウェアを示しています。RIPレジスタは、エントリポイントのアドレス0x1C3EEFEE0A8を指し示しています。デバッガーの下部には、アドレスを変更されたWindows API関数も表示されています。

図7:固定APIテーブルを使用してエントリポイント関数の位置でブレーク

・メモリをさらに割り当てる

 分析の結果、このマルウェアにはアドレス0x1C3EEB7000にあるグローバル変数データ(サイズは0x5A000バイト)も必要であることがわかりました。

 そのため、Volatilityツールとddコマンドを使用して、必要なグローバルデータをfulloutから抽出しました。ここで再びVirtualAlloc() APIを呼び出して、dllhost.exeプロセス内の目的のアドレスに、必要なサイズの新たなメモリ領域を割り当てました。割り当ての完了後、図8に示すように、抽出したデータが新たに割り当てたメモリ領域にコピーされました。

図8:アドレス0x1C3EEB7000から始まるコピーされたグローバル変数データ

・パラメータをエントリポイントとスタックに設定する

 マルウェアのエントリポイント関数の静的分析により、その関数が3つのパラメータを必要とすることがわかりました。

1. 最初のパラメータ(RCX)は読み込んだマルウェアのベースアドレスで、この場合は0x1C3EEFB0000です。
2. 2番目のパラメータ(RDX)の値は0x1です。
3. 3番目のパラメータ(R8)は、0x30バイトのバッファへのポインタですが、これもfulloutファイルから抽出することができます。

 私たちはこの3つのパラメータをすべて適切に準備し、図9に示すように、3番目のパラメータの30Hデータを保存するために追加のメモリを割り当てました。

図9:準備された3つすべてのパラメータと調整されたRSPレジスタ

 最後の重要な項目は、RSPレジスタのアライメントです。エントリポイントでブレークするときのRSP値の下位4ビットは、図9に示すように0x8でなければなりません。RSPを適切にアライメントできなかった場合、マルウェアの起動時に例外EXCEPTION_ACCESS_VIOLATION(コード0xC0000005)がトリガーされる可能性があります。

 これは、特に16バイトのアライメントが必要な“movdqa”などの命令を実行するときに、不整合エラーが発生するためです。64ビットコードモードでは、エントリポイント関数を呼び出す前に、RSPの値が16バイトでアライメントされます(下位4ビットは0x0)。戻りアドレスがスタックにプッシュされ、RSP値がマイナス8になります。これを修正するために、値を0x09CAD11D850から0x09CAD11D848に調整しました。

マルウェアの分析

 何回もの試行、エラー、および度重なる修正の後、ついにマルウェアをローカル環境内で実行することに成功しました。

 このマルウェアは実行時に、メモリ内に保存されたC2サーバードメイン情報を復号するための関数を呼び出します。図10には、復号関数と、新たに復号されたドメイン詳細が表示されています。ドメインは“rushpapers.com”で、ポート番号は"443"です。

図10:C2サーバー情報が復号されたときの画面

 マルウェアはその後、スレッドを作成してC2サーバーとの通信を確立します。図11に示すように、マルウェアは0x1C3EEFDE300にあるスレッド関数を使用して、CreateThread() APIを呼び出す準備をします。

図11:C2通信のためにスレッドが作成されようとしています。

 新たに作成されたスレッドは、C2サーバーとの通信を処理する役目を果たします。スレッドのコードの一部が図11の右側に表示されています。スレッドの起動後、メインスレッドは通信スレッドの実行が完了するまでスリープ状態に入ります。

C2サーバーとの通信

 復号されたドメインポート“443”は、マルウェアとC2サーバーとの通信にTLSプロトコルが使用されることを示しています。マルウェアはgetaddrinfo() APIを使用して、ドメイン“rushpapers.com”のIPアドレスをDNSクエリを介して取得します。

 図12は、マルウェアがC2サーバーと通信するときに生成したネットワークトラフィックが表示されている、Wiresharkのキャプチャ画面です。

図12:マルウェアとC2サーバーの間で交換されたネットワークパケット

 TLSパケットは暗号化されているため、平文の中身を見るには、暗号化前か復号後にデータを調べる必要があります。それには、このマルウェアが使用する暗号化ルーチンと復号ルーチンの両方に、デバッガー内でブレークポイントを設定します。

 TLSトラフィックの暗号化/復号には、2つのAPI関数、SealMessage()とDecryptMessage()が使用されています。図13では、マルウェアがSealMessage() APIを使用してHTTP GETリクエストを暗号化する準備をしています。

図13:SealMessage()を使用してGETリクエストの暗号化を準備

 平文パケットの例を以下に2つ示します。一方は送信、もう一方は受信されたパケットです。

・リクエストパケット(TLS暗号化の前)

GET /ws/ HTTP/1.1

Host: rushpapers[.]com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: OCnq155rYct3ykkkdLrjvQ==


・応答パケット(TLS復号の後)

HTTP/1.1 101 Switching Protocols

Server: nginx/1.18.0
Date: Fri, 28 Mar 2025 06:13:24 GMT
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Bzr0K1o6RJ4bYvvm4AM5AAG172Y=


 この2つの平文パケットは、ハンドシェイクに似たプロセスを完了するために使用されます。その後は、独自の暗号化アルゴリズムに切り替えて、TLS暗号化を適用する前にパケットデータを暗号化します。

 独自のアルゴリズムを使用して暗号化されたデータの例を以下に示します。

00000000   82 A6 16 98 5C 75 59 CB  66 55 41 F1 32 11 79 EF   ‚¦ ˜\uYËfUAñ2 yï
00000010   2F 55 27 A8 7C 5A 36 AE  68 58 74 F1 28 55 3E A9   /U'¨|Z6®hXtñ(U>©
00000020   6C 5B 26 B6 6D 4C 26 AC  69 5C 1B 92                       l[&¶mL&¬i\ ’

 このデータをもっとわかりやすく分類したものを以下の表に示します。

オフセット 長さ 説明
00 01 マジックタグ 0x82
01 可変 拡張可変長このケースでは0xA6. 
02 04 暗号鍵 16 98 5C 75
06 26H 暗号化されたデータ 59 CB  66 … 1B 92

 拡張可変長ルールに従って、データのサイズは0xA6-0x80=0x26と算出できます。
暗号鍵(例えば0x755C9816)はランダムに生成される数値です。独自の暗号化アルゴリズムは、鍵の各バイトと暗号化されたデータバイトとの間でXOR演算を繰り返し実行します。

 その結果、復号されたデータは以下のとおりです。

00000000   4F 53 3A 20 57 69 6E 64  6F 77 73 20 31 30 20 2F   OS: Windows 10 /
00000010   20 36 34 2D 62 69 74 20  28 31 30 2E 30 2E 31 39   64-bit (10.0.19
00000020   30 34 35 29 0D 0A                                  045)

 
 復号されたデータを見ると、ローカルテスト環境から取得したシステム情報であることがわかります: "OS: Windows 10 / 64-bit (10.0.19045)\r\n"。この情報は、収集された後、C2サーバーから要求されたときにC2サーバーへ送信されます。
データの暗号化と復号に使用される独自のアルゴリズムを示しているコードスニペットを以下に示します。

[…]
001C3EF00CED3 loc_1C3EF00CED3:    ; CODE XREF: sub_1C3EF00CE08+FD↓j
001C3EF00CED3     mov  eax, r9d       
001C3EF00CED6     and  eax, 80000003h
001C3EF00CEDB     jge  short loc_1C3EF00CEE4
001C3EF00CEDD     dec  eax
001C3EF00CEDF     or   eax, 0FFFFFFFCh
001C3EF00CEE2     inc  eax
001C3EF00CEE4
001C3EF00CEE4 loc_1C3EF00CEE4:  ; CODE XREF: sub_1C3EF00CE08+D3↑j
001C3EF00CEE4     mov  ecx, [rbx+30h]
001C3EF00CEE7     add  ecx, r9d
001C3EF00CEEA     cdqe
001C3EF00CEEC     inc  r9d
001C3EF00CEEF     mov  r8b, byte ptr [rsp+rax+28h+arg_0] ;;ランダム鍵
001C3EF00CEF4     xor  r8b, [r10]  ;;;;;;; ランダム鍵を使ってデータを暗号化 / 復号
001C3EF00CEF7     inc  r10
001C3EF00CEFA     mov  rax, [rbx+20h]
001C3EF00CEFE     mov  [rcx+rax], r8b
001C3EF00CF02     cmp  r9d, edi     ; ediはデータサイズ
001C3EF00CF05     jl   short loc_1C3EF00CED3
001C3EF00CF07
001C3EF00CF07 loc_1C3EF00CF07:  ; CODE XREF: sub_1C3EF00CE08+C6↑j
001C3EF00CF07     add  [rbx+30h], edi
001C3EF00CF0A     jmp  loc_1C3EF00CE55
[…]

機能分析

 API呼び出しと実行フローの包括的な分析により、このマルウェアがRAT(リモートアクセス型トロイの木馬)であることが判明しました。このセクションでは、マルウェアに侵害されたシステムを制御する機能について詳しく解説します。

・スクリーンショットのキャプチャ

 このマルウェアは、被害者の画面をJPEG画像としてキャプチャしてC2サーバーに送信する機能を備えています。また、現在のアクティブな(一番上の)プログラムのタイトルも収集して、キャプチャの時点でユーザーが何をしていたかを示すコンテキストを提供します。

 そのために、CreateStreamOnHGlobal()、GdiplusStartup()、GetSystemMetrics()、CreateCompatibleDC()、CreateCompatibleBitmap()、BitBlt()、GdipCreateBitmapFromHBITMAP()、GdipSaveImageToStream()、GdipDisposeImage()などを含む一連のAPIを呼び出します。

 図14は、マルウェアがこれらのAPIをどのように呼び出してスクリーンショットをキャプチャするかを示しています。
 

図14:スクリーンショットをキャプチャする関数の擬似コード

・サーバーとして動作

 このマルウェアには、サーバーとして動作するように設計されたスレッド関数が含まれており、C2サーバーから指定されたTCPポートでリッスンします。この関数はアクティブになると、マルウェアが攻撃者からの着信接続を待機できるようにします。

 そのためにマルチスレッドソケットアーキテクチャを実装し、新しいクライアント(攻撃者)が接続するたびに、マルウェアが新しいスレッドを生成して通信を処理します。この設計により同時セッションが可能になり、より複雑な命令をサポートできます。

 マルウェアはこのモードで動作することで、侵害されたシステムを効果的にリモートアクセスプラットフォームに変えます。その結果、攻撃者はさらなる攻撃を開始したり、被害者の代わりにさまざまなアクションを実行できるようにします。

・システムサービスを制御

 このマルウェアは、感染したマシン上のシステムサービスを列挙して操作することができます。そのために、OpenSCManagerW()、EnumServicesStatusExW()、ControlService()などを始めとするWindows Service Control Manager(SCM)APIが使用されます。

結論

 この分析では、DOSヘッダーとPEヘッダーが破損したマルウェアの展開と動的分析を制御されたローカル環境内にデプロイメントすることに成功しました。

 マルウェアの実行準備(メモリの割り当てやAPIの解決など)から実行パラメータの修正までの詳細なプロセスにより、マルウェアの振る舞いを正確にエミュレートできました。

 ペイロードの調査により、SealMessage()およびDecryptMessage() APIを使用した安全な暗号化 / 復号メカニズムなど、C2サーバーとの高度な通信が明らかになりました。
最後に、画面キャプチャ、リモートサーバー機能、Service Control Manager APIを使ったシステムサービスの操作など、侵害されたシステムに対するマルウェアの重要な機能を確認しました。

 フォーティネットはこれらの攻撃からの保護を提供しており、必要なときにはFortiGuard IRチームのサービスをいつでもご利用できます。

フォーティネットのソリューション

 フォーティネットのお客様は、FortiGuardのアンチウイルス、Webフィルタリング、アンチボットネットサービスによって、このマルウェアから以下のように保護されています。

・FortiGuardアンチボットネットサービスは、C2サーバードメインへのDNSリクエストをブロックします。
・FortiGateは、C2サーバーが使用する悪意あるTLS証明書をブロックします。
関連するC2サーバーのURLは、FortiGuard Webフィルタリングサービスにより「悪意あるWebサイト」として識別されます。

 FortiGate、FortiMail、FortiClient、FortiEDRは、FortiGuardアンチウイルスサービスをサポートしています。これらの各ソリューションには、FortiGuardアンチウイルスエンジンが含まれています。

 したがって、最新の保護機能を備えたこれらの製品をお使いのお客様は、すでに脅威から保護されています。

 新たに発生する脅威の最新情報を入手するには、アウトブレイクアラートにご登録ください。今後のアラート情報をお届けします。

 また、読者の皆様には、フォーティネットが無償で提供するNSEトレーニング:NSE 1 – 情報セキュリティ意識向上を受講することをお勧めします。このトレーニングにはインターネットの脅威に関するモジュールが含まれ、エンドユーザーは各種のフィッシング攻撃を識別して自らを保護する方法を学習できます。

 組織がこの種の攻撃や他のサイバーセキュリティ攻撃を受けていると思われる場合は、フォーティネットのグローバルFortiGuardインシデントレスポンスチームまでご連絡ください。

IOC(Indicators of Compromise:侵害指標)

URL

hxxps[:]//rushpapers[.]com/ws/

Sha256

3EB67B8DDAC2732BB8DCC07C0B7BC307F618A0A684520A04CFC817D8D0947B9

■関連サイト