FLARE スクリプト シリーズ: FireEye Labs Query-Oriented Debugger (flare-qdb) を使用した動的状態のクエリ

序章

この投稿は、FireEye Labs Advanced Reverse Engineering (FLARE) スクリプト シリーズの続きです。ここでは、コマンドライン ユーティリティであり、動的なバイナリ状態を便利に、反復的に、大規模に照会および変更する vivisect に基づく Python モジュールである、flare-qdb を紹介します。 Flare-qdb は Windows と Linux で動作し、 flare-qdb github プロジェクトから入手できます。

動機

複雑または難読化されたマルウェアを効率的に理解するには、デバッグが必要になることがよくあります。多くの場合、プログラム カウンターをたどる線形プロセスは、並列または前のレジスター状態、ループ内の状態変化、または命令がまったく実行されるかどうかについて疑問を投げかけます。たとえば、レジスターの値は、それが既に実行された関数への重要な入力であることをアナリストが知るまで、興味深いものにはならないかもしれません。デバッグ セッションを再開し、結果をカタログ化することで、アナリストは疑問が生じた元の思考プロセスから抜け出すことができます。

マルウェア アナリストは、そのような調査が不可欠な観察や無関係な気晴らしに役立つかどうかを常に判断する必要があります。間違った決定は、貴重な時間を無駄にします。次のように、データベースのようにマルウェアの状態を照会すると便利です。

SELECT eax, poi(ebp-0x14) FROM malware.exe WHERE eip = 0x401072

FLARE は、同様の方法で動的状態などを効率的にクエリするためのコマンドライン ツールを考案しました。以下は、このツールが FLARE 内でどのように使用され、マルウェアを分析し、新しいシナリオをシミュレートし、さらには 2016 FLARE-On チャレンジからの課題を解決したかの例とともに、このツールの説明です。

使用法

Flare- qdbは、 vivisect から多くのことを引き出しており、洗練された質問を効率的に提起し、バイナリから簡単な回答を取得するためのオープン ソース ツールです。 Flare-qdb のコマンドライン構文は次のとおりです。

Flareqdb “<コマンドライン>” -at <アドレス> “<python>”

アナリストは、flare-qdb を使用して、選択したコマンド ラインを実行し、任意のプログラム カウンター値で中断し、オプションで条件をチェックし、アドホック Python コードを使用してプログラムの状態を表示または変更できます。 Flare-qdb は、状態のクエリと変更のためにいくつかの WinDbg のようなビルトインを実装しています。表 1 に、クエリの例をいくつか示します。

実験または変更

クエリ

kernel32!Beep に渡される 2 つの DWORD 引数は? (WinDbg アナログ: dd)

-at kernel32.Beep “dd(‘esp+4’, 2)”

0x401072 で eax が null の場合は終了します (WinDbg アナログ: .kill)

-at-if 0x401072 eax==0 “kill()”

プログラムで ecx を変更する (WinDbg アナログ: r)

-at マルウェア モジュール + 0x102a “r(‘ecx’, ‘(ebp-0x14)*eax’)

プログラムでメモリを変更する

-at 0x401003 “memset(‘ebp-0x14’, 0x2a, 4)”

表 1: Flare-qdb クエリの例

Flareqdb コマンド ラインの使用

文字列を扱うループなどで、flare-qdb の有用性が見られます。図 1 は、flareqdb コマンド ライン ユーティリティを使用して、ループの反復ごとにスタック変数が指す Unicode 文字列をダンプする様子を示しています。出力は、変数が argv[1] を反復するランナー ポインターとして使用されていることを示しています。

Using flareqdb to monitor a string within a loop
図 1:flareqdb を使用してループ内の文字列を監視する

もう 1 つの例は、2016 年の FLARE-On Challenge の課題 4 です (ネタバレ注意: 部分的な解決策を以下に示します。完全なチュートリアルはこちら)。

Flareon2016challenge.dll では、デコードされた PE ファイルに kernel32!Beep への一連の呼び出しが含まれており、チャレンジ バイナリの序数 #50 への呼び出しの正しいシーケンスを構築するために追跡する必要があります。図 2 は、各 kernel32!Beep 呼び出しをチャレンジ バイナリの序数 #50 に転送してフラグを取得する、flareqdb ワンライナーを示しています。

2016年FLARE-On Challengeの課題4を解決するためにflareqdbを使用する
図 2:flareqdb を使用して 2016 FLARE-On Challenge の課題 4 を解決

また、flareqdb は強制的に分岐させたり、関数ポインター値を評価したり、逆アセンブルによって疑わしい関数アドレスを検証したりすることもできます。たとえば、一連の条件が満たされた場合にのみ呼び出され、C++ 仮想関数を呼び出す図 3 のサブルーチンを考えてみましょう。この機能を特定することは、アナリストがその呼び出し元を特定し、それを実行するためにコマンド アンド コントロール (C2) チャネルを介して提供するデータの種類を発見するのに役立ちます。

仮想関数呼び出しで不明な関数
図 3: 仮想関数呼び出しによる未確認の関数

Flareqdb コマンドライン ユーティリティを使用すると、プログラム カウンターを流用して、提供された C2 データのチェックをバイパスし、続いてプログラム カウンター 0x4029a4 でマルウェアによって呼び出された関数ポインターのアドレスをダンプすることができます。 vivisect のおかげで、flare-qdb は結果のアドレスで命令を逆アセンブルして、それが実際に関数であることを検証することさえできます。図 4 は、flareqdb コマンドライン ユーティリティを使用して、0x4016b5 の制御フローを強制的に 0x4016bb (表示されていません) に進め、その後、0x4029a4 で呼び出された関数ポインターをダンプすることを示しています。

分岐の強制と C++ 仮想関数呼び出しの解決
図 4: 分岐を強制し、C++ 仮想関数呼び出しを解決する

関数ポインターは 0x402f32 に解決され、図 5 に示すように、IDA は既に basic_streambuf::xsputn というラベルを付けています。この関数は一連の文字をファイル ストリームに挿入します。 /または C2 チャネル経由のファイル データ。

解決された仮想機能アドレス
図 5: 解決済みの仮想機能アドレス
Flareqdb Python モジュールの使用

また、flare-qdb は、より複雑なケースに役立つ Python モジュールとしても存在します。 Flare-qdb を使用すると、強力な vivisect ライブラリをすぐに使用できます。特権エスカレーション ツールの一部である図 6 のロジックを考えてみましょう。このツールは、WMI で CVE-2016-0040 を悪用する前に、GetVersionExW、NetWkstaGetInfo、および IsWow64Process をチェックします。

権限昇格プラットフォームのチェック
図 6: 権限昇格プラットフォームのチェック

このツールは、バージョン番号 5.1+、6.0、および 6.1 の 32 ビット Windows インストールを悪用しているように見えます。図 7 は、ツールを 12 回実行し、GetVersionExW と NetWkstaInfo から返されるさまざまなバージョンをシミュレートして、これを迅速に検証するスクリプトを示しています。スクリプトがマルウェアを実行するたびに、マルウェアが権限昇格を試みる段階に達したかどうかが示されます。スクリプトは、実行ごとにローカル変数の辞書を Qdb インスタンスに渡し、ユーザー コールバックがバイナリ用にシミュレートしている各 Windows バージョンのフレンドリ名を出力できるようにします。 GetVersionExW の結果は、OSVERSIONINFOEXW の vstruct 定義を使用して、返される前に変更されます。 NetWkstaGetInfo は、簡潔にするために手動で修正され、WKSTA_INFO_100 構造体に対応する定義がない場合に修正されます。

バージョン チェックをテストするスクリプト
図 7: バージョン チェックをテストするスクリプト

図 8 は、図 6 のロジックの分析を確認する出力を示しています。

スクリプト出力
図 8: スクリプト出力

次に、アナリストが反復可能なプロセスを考案してバイナリをアンパックし、メモリ全体に注入されたアンパックされた PE-COFF ファイルの場所を確認する必要がある例を考えてみましょう。図 9 のスクリプトは、テール コールに関連するブレークポイントを設定し、vivisect の Environment モジュールを使用して、名前付きファイルによってサポートされていないすべての RWX メモリ ロケーションを列挙することでこれを行います。次に、detach() を呼び出す前に、flare-qdb の park() ビルトインを使用して、バイナリが無限ループで実行されるようにし、アナリストがデバッガーをアタッチして手動分析を再開できるようにします。

アンパックの完了後にデバッグ対象をパークするアンパッカー スクリプト
図 9: アンパックの完了後にデバッグ対象をパークするアンパッカー スクリプト

図 10 は、プロセスを無限ループに入れてデタッチする前に、自己注入モジュールの場所をアナウンスするスクリプトを示しています。

アンパッカースクリプトの結果
図 10: unpacker スクリプトの結果

図 11 のように、WinDbg を介して IDA Pro にアタッチすると、プログラム カウンターが、flare-qdb によって割り当てられたメモリに書き込まれた無限ループを指していることがわかります。 park() ビルトインは、元のプログラム カウンター値を jmp 命令に続くバイトに格納しました。アナリストは、これらのバイトを参照して WinDbg コマンド r eip=1DC129B を入力することにより、プログラムを元の場所に戻すことができます。

パークされたプロセスへのアタッチ
図 11: パークされたプロセスへのアタッチ

パークされたプロセスにより、マルウェア実行 VM のスナップショットを作成し、IDA Pro をデバッガとして使用してさまざまなコード領域を実行し、注釈を付けるためにリモートで繰り返し接続することが容易になります。同じ OS プロセスを複数のデバッグ セッションで再利用できるため、スクリプトによってアナウンスされるメモリ マップはデバッグ セッション間で同じままです。つまり、IDA Pro で作成された注釈は、プログラムが単純に複数回実行された場合に VirtualAlloc によって返される非決定論的なヒープ アドレスに起因するさまざまなデータやコードの場所から切断されるのではなく、適切なままであることを意味します。

結論

Flare-qdb は、進行中のデバッグ セッションの思考プロセスを脱線させることなく、動的なバイナリ状態をすばやくクエリするためのコマンドライン ツールを提供します。状態のクエリに加えて、flare-qdb を使用してプログラムの状態を変更し、新しいシナリオをシミュレートできます。複雑なケースでは、flare-qdb にはスクリプト インターフェイスがあり、ほぼ任意の操作が可能です。これは、文字列のデコード、マルウェアのアンパック、および一般的なソフトウェア分析に役立ちます。使用を開始するには、 flare-qdb github ページにアクセスしてください。

参考: https ://www.mandiant.com/resources/blog/flare_script_series

コメント

タイトルとURLをコピーしました