Windows 内でRPC を守る機能
目次
このブログの内容は、初めに Blackhat USA 2023 で発表されました。
MS-RPC:攻撃を検知するためのプロトコルのほころび
私たちは継続的に MS-RPC に関する調査を行っており、他のブログ記事でも取り上げています。今回は、攻撃面にフォーカスして脆弱性を探すのではなく、Windows に組み込まれている防御機能について説明します。そして、それを活用して、RPC の水面下で何が起こっているのかを把握し、できればその過程で不正なアクティビティを検知する方法について取り上げます。
MS-RPC は Windows オペレーティングシステムに不可欠であり、そのため多くのラテラルムーブメント(横方向の移動)パスが MS-RPC を通ります。 PSExec、 Remote Scheduled Task、DCSync、 PetitPotam といった攻撃はすべて、MS-RPC プロトコルを介して実行されているため、従来のネットワークメタデータ(送信元ポート、宛先ポート、IP アドレス。プロセス情報の場合もある)だけで良性のネットワークトラフィックと悪性のネットワークトラフィックを識別するのは困難です。
Windows オペレーティングシステムに組み込まれている追跡監視メカニズムである Event Tracing for Windows(ETW)を使用すると、可視性が高まります。ETW は、特に従来のネットワークメタデータと比較した場合に、RPC に関する貴重な情報を提供します。
ETW プロバイダーを利用して RPC イベントを追跡するという概念は新しいものではなく、そのために利用できる優れたリソースやツールがすでにたくさんあります( 参考文献 セクションにその一部のリンクを記載しています。その他のリソースやツールがあればお知らせください)。 ほとんどの既存の調査は、クライアント側のイベントにフォーカスしています。そのようなイベントでは、攻撃者はプログラムを改ざんまたは変更し、ログを完全に回避することができます。。それに対し、ここでは攻撃者のリーチ外であるサーバー側のイベントを利用した攻撃の検知にフォーカスし、それを解析するために必要な固有の留意事項を確認します。
この記事では、RPC ETW プロバイダーからのイベントを利用し解析する方法と、悪性である可能性のあるアクティビティがないかイベントをスキャンする方法について説明します。ETW プロバイダーを利用すると、要求されている正確なオペレーションを確認することもでき、よりきめ細かく攻撃を検知できるようになります。その仕組みについても後ほど説明します。
振り返り:RPC とは
RPC とは、リモート・プロシージャー・コールの略であり、プロセス間通信(IPC)の一形態です。具体的には、このプロトコルは異なるプロセス間でリモート関数を呼び出すことができるように設計されています。ここでは、Microsoft の実装である MS-RPCを取り上げます。
MS-RPC はクライアントサーバーモデルとして設計されています。サーバーは、インターフェース定義言語(IDL)を使用して、公開するインターフェースを定義します。IDL 内には、インターフェースの汎用一意識別子(UUID)と、それが公開する関数の関数定義があります(図 1)。
[
uuid(12345678-4000-2006-0000-20000000001a)
]
interface Test
{
void Foo([in] int number, [in] char *message);
void Bar([out] int * result);
}
図 1:IDL インターフェース定義の例
通信は特定のトランスポートプロトコルを介して行われ、トランスポートごとに固有のエンドポイントがあります(図 2)。わかりやすい例を挙げるとすれば、RPC 通信は TCP を介して伝送でき、この場合のトランスポートは TCP で、エンドポイントはポート番号で識別される TCP ソケットです。
トランスポート |
エンドポイント |
---|---|
TCP 名前付きパイプ UDP ALPC HTTP Hyper-V ソケット |
<ポート番号> <パイプ名> <ポート番号> <ALPC ポート> <ホスト名> <UUID> |
図 2:一般的なトランスポートプロトコルとそれぞれのエンドポイントタイプ
IDL ファイル内には読み取り可能な関数名がありますが、ネットワーク上では異なる方法で関数が識別されることに注意してください。文字列名ではなく、opnum(operation number の略)と呼ばれる整数で識別されます。これは通常、IDL インターフェース定義内の関数の出現順序に従って割り当てられます(図 1 の例では、Foo が opnum 0、Bar が opnum 1 で識別されます)。これは後々、関連する関数の opnum を把握して、表示されるデータ内で関数を識別する必要がある場合に重要になります。
MS-RPC のさらに詳しい概要については、 過去の記事を参照してください。または、 HexaCon や DEF CON におけるこのトピックに関する Akamai のカンファレンスプレゼンテーションをご覧ください。
ETW の定義
Microsoft-Windows-RPC
RPC ETW プロバイダー は、RPC ランタイム「rpcrt4.dll」内に実装されます。13 種類のイベントがありますが、ここで主に着目するのは、イベント 5 と 7(クライアントコールの開始と停止)、イベント 6 と 8(サーバーコールの開始と停止)の 4 つのイベントです。情報量が最も多いのはコール開始イベントなので、コール開始イベント(図 4)にフォーカスします(コール停止イベントは、単に RPC リターンステータスを示します)。クライアントイベントとサーバーイベントは同じ形式を共有します。
<template tid="RpcServerCallStartArgs_V1">
<data name="InterfaceUuid" inType="win:GUID"/>
<data name="ProcNum" inType="win:UInt32"/>
<data name="Protocol" inType="win:UInt32"/>
<data name="NetworkAddress" inType="win:UnicodeString"/>
<data name="Endpoint" inType="win:UnicodeString"/>
<data name="Options" inType="win:UnicodeString"/>
<data name="AuthenticationLevel" inType="win:UInt32"/>
<data name="AuthenticationService" inType="win:UInt32"/>
<data name="ImpersonationLevel" inType="win:UInt32"/>
</template>
図 4:RPC ETW コール開始イベントスキーマ
理論的には、このイベントのデータには、RPC トラフィックに関する知見を得るために必要なすべてのメタデータが含まれています。これにより、インターフェース UUID および opnum から何が要求されているのかがわかります。また、NetworkAddress フィールドにより、送信元アドレスまたは宛先アドレス(クライアントイベントまたはサーバーイベントのどちらを見ているかによって異なる)もわかります。そんなに簡単なはずはないと思いますよね。
そのとおりです。サーバーコールイベントの処理時には RPC ランタイムが NetworkAddress フィールドへの入力を行わないため、接続メタデータが必要な場合は、別の方法でそのデータを見つける必要があります。クライアントイベントでは、このフィールドへの入力があります。
これにより、次のような疑問が発生します。サーバーイベントを無視して、クライアント側だけを見ればよいのだろうか。答えは Noです。ここでは、攻撃者によって制御されているマシンから生じる悪性のふるまいを見つけようとしているため、 攻撃者がクライアント側の ETW プロバイダーに変更を加えない(そして、クライアント側の ETW プロバイダーをオフにしたり、イベントをブロックしたりしない)という確信は得られませんし、攻撃者が RPC ランタイムを実行するという確信も得られません。
一般的な Python ライブラリである Impacketは、攻撃の概念実証(PoC)やツールでよく使用され(また、 PSExecなどのネットワーク攻撃の実装を含みます)、その中に RPC トラフィックを実装します。そのため、これを使用する攻撃者は RPC ランタイムを回避し、ETW プロバイダーに登録されません。 サーバーイベントは攻撃者のコントロール外にあるため、サーバーイベントを利用する方が賢明です。そのため、ここでは、ネットワークデータを他の場所から取得して RPC イベントにマッチングする方法を取り上げます。
RPC イベントをネットワークフローにマッチング
RPC ETW プロバイダーに関する説明は一旦置いておいて、他の ETW プロバイダー、すなわち TCP プロバイダーと SMB プロバイダーについて見ていきます。これら 2 つのプロトコルは、RPC トラフィックの一般的なトランスポートプロトコルです。前述のとおり、RPC エンドポイントタイプはトランスポートプロトコルに依存するため、RPC プロバイダーから受信したエンドポイント(ポート番号、パイプ名など)をトランスポート ETW プロバイダーのそれぞれの値にマッチングすることができます。
TCP の場合、非常に簡単です。イベント ID 1017( TcpAcceptListenerComplete)を見てみましょう。これは、TCP 3 ウェイハンドシェイクが完了するとトリガーされます。
これには(基本的に) LocalAddress と RemoteAddress の 2 つのフィールドがあります(ただし、ここではサーバーイベントを取り上げているため、ローカルはサーバーを指し、リモートはクライアントを指します)。アドレスフィールドの値はバイナリ形式で、アドレスファミリー、IP アドレス、およびポート番号が含まれています(図 6)。
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
---|---|---|---|---|---|---|---|
アドレスファミリー |
ポート番号 |
アドレス値(AF_INET の場合は IP) |
図 6:アドレス・バイナリ・フィールドの形式
必要なのは、(クライアントの)IP アドレスを RemoteAddress フィールドから抽出し、 LocalAddress フィールド内の(宛先)ポートを使用して追跡することだけです。サーバー上の同じ TCP ポート番号で RPC イベントが発生すると、TCP プロバイダーから抽出したポートと IP のマッチングに基づいて、その発生元を確認できます(図 7)。
SMB では、ETW イベントごとに異なる情報が表示されるため、状況はさらに複雑になります。エンドポイントは名前付きパイプ(ファイルと同じ)ですが、同じ SMB セッションで複数のファイルにアクセスできます。この場合、エンドポイントをそのネットワークソースとマッチングするためには、実際の接続用とファイル要求用の 2 つのイベントを追跡する必要があります(図 8)。
接続イベントの場合、イベント ID 500( Smb2ConnectionAcceptStart)があり、SMB 接続が確立されるとトリガーされます。そこから、ソース IP と接続 UUID が取得されます。次に、イベント ID 8( Smb2RequestCreate_V2)を探します。これには、要求されたファイル名と、同じ接続 UUID フィールドが含まれます。次に、接続 UUID を介して両方のイベントを相互参照して、パイプ名をそれを要求した IP にマッチングするだけです(その後、パイプ名を RPC コールエンドポイントにマッチングする必要があります)。
このようなマッチングを自分で実行する手間を省くために、Akamai はこのアルゴリズムを実装しました。また、Akamai の GitHub リポジトリには RPC Visibility ツールがあります。このツールは Python で記述されており、記録された RPC ネットワークトラフィックを Neo4j データベースに保存して、簡単に視覚化できます。
攻撃検知
これで必要な情報がすべて揃ったので、悪性の RPC フローの検知に目を向けられるようになりました。これは一般的でシンプルなプロセスです。RPC を介して伝送される攻撃を見つけて、その PoC を実行し、使用される RPC インターフェースと要求されるオペレーションを確認します。その後、そのオペレーションに合ったシグネチャーを作成するだけです。リリースには Neo4j データベース実装で動作するシグネチャークエリーを含めましたが、一般的なロジックと条件については以下をお読みください。
PSExec
PSExec は、攻撃手法の一般的な名称であり、Sysinternals ツール(その手法の名前の由来となったツール)でもあります。基本的に、このツールはサービスバイナリをリモートターゲットの ADMIN$ 共有(Windows のインストールフォルダ)にコピーし、続いて RPC インターフェース(MS-SCMR)を介してサービスマネージャーと通信し、バイナリのサービスを作成して実行します(図 9)。
リモートマシンに SCMR 経由でコンタクトする正当な理由は数多くあります。たとえば、リモートサービスのステータスを照会するウォッチドッグなどです。しかし、リモートで新しいサービスを作成する理由ははるかに少ないです。したがって、SCMR 経由のあらゆる接続に対してアラートをトリガーする必要はなく(受信ネットワーク接続をサービス・マネージャー・プロセス「 services.exe」にマッチングするだけで、RPC ETW なしでも検知できます)、リモートサービスを作成する接続に対してのみアラートをトリガーする必要があります。
そこで、シグネチャーは(RPC 可視性の実装に固有のものではなく、大まかに)次のようにする必要があります。
interface_uuid == “367ABB81-9844-35F1-AD32-98F038001003” AND (opnum == 0xC OR opnum == 0x18)
0xC は RCreateServiceW の opnum、0x18 は RCreateServiceAの opnum です。
リモート・タスク・スケジューラー
PSExec と同様に、Task Scheduler を使用してリモートバイナリを起動し、このようにラテラルムーブメントを実行することができます。新しいバイナリを起動する必要はありません。cmd または PowerShell コンソールを起動し、オンラインでホストされているバイナリをダウンロードするだけです。
また、PSExec と同様、Task Scheduler サービスへのあらゆるアクセスを検知する必要はなく、主に SchRpcRegisterTaskへの RPC コールに注目します。
interface_uuid == “86D35949-83C9-4044-B424-DB363231FD0C” AND opnum == 0x1
DCSync
DCSync も RPC ベースの攻撃ですが、ドメインコントローラーを標的とします。この場合、攻撃者は実際のドメインコントローラーに接続し、新しいドメインコントローラーになりすまします。次に、ドメインコントローラーのクレデンシャルデータベースを複製して、KRBTGT パスワードハッシュにアクセスできるようにします。
この複製要求は MS-DRSR RPC インターフェース経由で行われ、関数 DRSGetNCChanges (opnum 3)を使用します。
interface_uuid == “e3514235-4b06-11d1-ab04-00c04fc2dcd2” AND opnum == 0x3
PetitPotam
PetitPotam は、暗号化ファイルシステム(EFS)サービスに対する認証強制攻撃です。基本的に、攻撃者は EFS RPC インターフェース(MS-EFSR)に接続し、UNC パスで指定されたリモートファイルを開くように指示することができます。その後、攻撃者がリレーできる認証を使用してアウトバウンド SMB 接続をトリガーします。
攻撃 PoC がリリースされた際には EfsRpcOpenFileRaw (opnum 0)が使用され、その後パッチが適用されました。 Topotam(この脆弱性を発見した研究者)は、 EfsRpcEncryptFileSrv (opnum 4)にも同じ欠陥があることを発見しました。
interface_uuid == “c681d488-d850-11d0-8c52-00c04fd90f7e” AND (opnum == 0x0 OR opnum == 0x4)
欠点
RPC ETW プロバイダーからは多くの情報を収集できますが、すべてのセキュリティニーズに対応するワンストップショップではありません。各ネットワークフローでどのオペレーションが要求されるかというデータは貴重ですが、各要求で何のデータが渡されたかという最も重要な情報は記録されません。これは、ETW プロバイダーを介したラテラルムーブメント検知は、従来のネットワークの 4 タプルよりは多くのコンテキストを得られるものの、依然としてヒューリスティックベースのアプローチであることを意味しています。
また、これはあくまでも検知のための手法であり、攻撃の阻止や対処には使用できません。Microsoft は、RPC 用の別の組み込み防御メカニズムを提供しています。それは、Windows ファイアウォールの RPC フィルターです。このフィルタリングメカニズムの詳細と使い方については、Akamai の リモート・プロシージャー・コール(RPC)フィルターに関する決定版ガイドの記事を参照してください。
まとめ
RPC ETW プロバイダーは Windows に新たに追加されたものではありませんが、ほとんどの場合、ネットワーク防御を検討する際に無視されています。これとやり取りしてイベントを利用するツールがいくつかありますが、そのほとんどはセキュリティ研究者を対象としており、ネットワーク側にフォーカスしていません。
この記事では、TCP プロバイダーや SMB プロバイダーと一緒に RPC ETW プロバイダーを利用し、ネットワーク経由で要求される RPC オペレーションを可視化する方法について説明しました。これにより、ヒューリスティックベースのアプローチを通じて、ラテラルムーブメントに利用され得る悪性の要求を検知できます。
参考文献
他のセキュリティ研究者による RPC ETW コンシューマーツール
RpcMon (提供者:CyberArk)
RpcInvestigator (提供者:Trail of Bits)
RPC テレメトリの利用 (著者:Jonathan Johnson 氏、SpecterOps)
Akamai の GitHub リポジトリ (すべての RPC 向け)
Windows IPC の不愉快な実情 2:RPC (著者:Carsten Sandker 氏)
- Windows RPC サーバーのセキュリティを確保するためにすべきこと/避けるべきこと (著者:James Forshaw 氏)