Windows コンソール アクティビティの監視 (パート 2)

news

これは、過去の Windows コンソール アーキテクチャの実装について説明する 2 つのブログのうちの 2 つ目で、主に最新バージョンの Windows に存在する現在の実装に焦点を当てています。詳細については、最初のブログ「 Windows コンソール アクティビティの監視 パート 1 」をお読みください。

データのキャプチャ

コンソール データをキャプチャする方法を検討する前に、これがプロセス引数のキャプチャとどのように異なるかを理解しておくと役立つ場合があります。結局のところ、各プロセスのプロセス環境ブロック (PEB) を調べて、コマンド ライン引数を収集することはできないのでしょうか?これにより、侵害されたマシンでタスクを実行するときに攻撃者が使用したコマンド引数が表示されます。ただし、攻撃者が実行時に対話型のコマンド ライン ツールを使用している場合、このデータ構造では、攻撃者がツールに対して発行したコマンドを示すことができません。

たとえば、図 1 は、一般的な資格情報ダンプ ツールであるmimikatzのインスタンスを実行するために使用されるコマンド ラインを示しています。攻撃者は、資格情報の盗用やハッシュ ダンプなど、このツールのいくつかの機能のいずれかを使用する可能性があります。さらに、実行時の mimikatz の出力を確認できないため、攻撃者が侵害したユーザー アカウントを特定することは困難です。後で、コンソールからこのデータを収集することの価値と、このレベルのコンテキストがどのように役立つかを見ていきます。

 

mimikatz のインスタンスを実行するために使用されるコマンド ラインを示すプロセス エクスプローラー
mimikatz のインスタンスを実行するために使用されるコマンド ラインを示すプロセス エクスプローラー

これらすべてがどのように機能するかを理解したところで、データを傍受するにはどうすればよいでしょうか? Windows 7 以前でこれを行うのはかなり難しいようです。前述の ConsoleEvent オブジェクトと同期することでデータをキャプチャし、データを含むセクション オブジェクトからデータを読み取ることは (少し非現実的ではありますが) 可能であるように思われます。もう 1 つの方法は、コマンド データにアクセスするために必要な ALPC メッセージを送信することです。実際、kernel32.dll には、まさにそれを行うエクスポート API があります。 GetConsoleCommandHistory は、現在のコンソール セッションからコマンドを取得します。ここでの欠点は、この API を使用するには、クライアント プロセスのコンテキストで実行する必要があることです。これには、プロセス インジェクションまたはその他の不正な手段が必要です。これらの方法はどちらも特に洗練されたものではありません。

Windows 8 以降で使用されるコンソール ドライバーは、これをかなり簡素化します。ドライバーはデバイス オブジェクトを公開するため、それをアタッチしてフィルター処理できるはずです。しかし、その後は?機能がより詳細にどのように実装され、データがどのようにフォーマットされるかを理解する必要があります。

ドライバーのエントリ ポイントを見ると、かなり標準的なドライバーの初期化が見られます。ただし、興味深いのは、Fast I/O Device Control ディスパッチ関数の初期化です。この関数は、標準の IRP ベースの IRP_MJ_DEVICE_CONTROL ディスパッチ関数の前にカーネルによって呼び出されます。高速バージョンは、操作を完了としてマークするか、操作を戻して IRP を生成するかを選択できます。図 2 は、ConDrv の興味深いエントリ ポイントを示しています。

 

図 2: Windows コンソール ドライバー (ConDrv) のエントリ ポイント

ユーザー コードをざっと見てみると、関心のあるほぼすべてのデータが NtDeviceIoControlFile によって ConDrv に転送されていることがわかります。データは、NtReadFile と NtWriteFile を使用して ConDrv デバイス ハンドルにも転送されますが、これらの同じデータ バッファーは、Conhost が新しいデータを送信または要求しているときに、NtDeviceIoControlFile を使用して送信されます。したがって、FastIoDeviceControl ルーチンでのフィルタリングは、探している I/O データを放棄する必要があります (図 3)。

 

図 3: FastIoDeviceControlディスパッチ関数の関数プロトタイプ

この関数を分析すると、ユーザー モードに公開されている機能の大部分を含む IOCTL コードで渡された処理用の switch ステートメントが明らかになります。図 4 は、コンソールへのデータの読み取りと書き込みに関連する IOCTL コードを処理するコードを示しています。

 

図 4: ConDrv の IOCTL ハンドラー コード

ConDrv でサポートされている IOCTL コードはかなりの数ありますが、現時点では、表 1 にリストされているサブセットのみを対象としています。

IOCTL コード

関数

0x50000B

保留中の I/O を完了する

0x50000F

クライアントからデータを読み取ることができます (例: conhost.exe <- cmd.exe)

0x500013

データをクライアントに書き込むことができます (例: conhost.exe -> cmd.exe)

0x500037

新しい conhost.exe プロセスを起動します

表 1: ConDrv でサポートされている興味深い IOCTL ハンドラー

クライアントとサーバー間のデータの読み取りと書き込みを担当する 2 つの IOCTL コード (0x50000F と 0x500013) が最も興味深いものです。 condrv!CdpFastIoDeviceControl にブレークポイントを設定し、コマンド プロンプトにテキストを入力すると、conhost.exe のプロセス コンテキストでデバッガーが中断します。

図 3 のプロトタイプを使用して関数の引数を調べると、コマンド文字列を cmd.exe に送信するときに Conhost が提供した入力バッファー、サイズ、および IOCTL コードが示されます。これらの引数は、それぞれ黄色、青色、緑色で強調表示され、図 5 に示されています。 注: 入力バッファーは、Conhost にマップされたユーザー モード アドレスです。

 

図 5: condrv!CdpFastIoDeviceControl の関数パラメーターを調べる IOCTL

入力バッファーを逆参照すると、不透明なデータ構造が表示されます。入力バッファーを処理する ConDrv コードをさらに分析すると、データの形式を理解するのに役立ちます。この構造体の形式を図 6 に示します。注: これは文書化されていない構造体であり (私が知る限り)、そのフィールドは分析に基づいています。 ConDrv コードの。

Windows 10 システムの condrv.sys から推測されるメッセージ バッファー構造
Windows 10 システムの condrv.sys から推測されるメッセージ バッファー構造

各 I/O メッセージには、コマンド データのバッファとバッファ サイズが含まれています。出力では、この同じ構造を使用して、実行されたコマンドの結果が配信されます。図 7 は、この構造体定義をデータに適用して、Windbg でコンソール コマンドをキャプチャしたものです。ここでは、単純なディレクトリ リスト コマンドがコマンド プロンプトに入力されています。これらの同じ手順を使用して、コマンド出力をキャプチャすることもできます。

Windbg でのコマンド データの表示
Windbg でのコマンド データの表示

I/O データの形式とその転送方法を理解したので、概念実証フィルター ドライバーを作成してキャプチャし、処理のためにユーザー モードに送り返すことができます。このデータをインラインで取得するため、マシン上のすべてのコンソール セッションを再構築できるはずです。 DeviceConDrv にアタッチし、システム上のすべての Conhost プロセスの入力バッファーからデータをコピーします。次に、Python スクリプトを使用して、カーネル ドライバーから送信されたキャプチャされた I/O データを取得します。

まず、インタラクティブな Python セッションを実行しているユーザーのデータを完全にキャプチャできるかどうかを見てみましょう。図 8 は、ユーザーの Python セッションを示しています。下部のコマンド ウィンドウは、Python コードを実行する通常のユーザー セッションです。上のウィンドウは、ConDrv フィルター ドライバーからキャプチャされたデータを示しています。フロー (入力と出力) は、表 1 に示す IOCTL コードを使用して識別されます。これにより、データがドライバーにインラインで送信されるため、コマンドの正確なコンテキストが得られます。

Python コンソール セッションのキャプチャ。ユーザー セッションを下に示し、condrv.sys からキャプチャしたデータを上に示します。
Python コンソール セッションのキャプチャ。ユーザー セッションを下に示し、condrv.sys からキャプチャしたデータを上に示します。

この投稿の冒頭で、mimikatz と、攻撃者がコンソールを直接操作した場合にプロセス引数を読み取ってもコマンドが提供されないことについて説明しました。図 1 は、PEB コマンド ライン引数によって明らかにされるすべてが mimikatz プロセスのパスであることを示しています。図 9 は、この同じ mimikatz セッションからキャプチャされたコンソール I/O を示しています。これで、攻撃者がシステムから NT LAN Manager (NTLM) ハッシュを取得できたことを特定できます。いつ盗まれたのか、どのアカウントが侵害されたのかを正確に知ることさえできます。このようなアクティビティのタイムラインを持つことは、インシデント対応プロセス中に重要であり、他の手段 (プロセス メモリ ダンプなど) を介してこのデータを取得すると、貴重なコンテキストが不足します。

mimikatz コンソール セッションのキャプチャ。攻撃者のセッションを下に示し、condrv.sys から取得したデータを上に示します。
mimikatz コンソール セッションのキャプチャ。攻撃者のセッションを下に示し、condrv.sys から取得したデータを上に示します。

結論

これをすべてまとめると、Windows 8 以降のシステムですべてのコンソール入力と出力を傍受、変更、または完全に拒否できるようになりました。これは、システム ユーティリティからの I/O だけでなく、コンソールを使用するすべてのプログラムからの I/O をキャプチャするために使用できます。これは、Python、Ruby、Perl などの対話型インタープリターから、mimikatz や Metasploit などのセキュリティ ツールにまで及びます。攻撃者は侵害したシステムに対してより厚かましく行動し続けるため、この活動を特定できることは依然として重要です。

参照: https://www.mandiant.com/resources/blog/monitoring-windows-console-activity-part-2

Comments

Copied title and URL