優れた RPC インターフェースとその検索方法
編集・協力:Tricia Howard
はじめに
この数か月間、当社のチームでは MS-RPC の調査に特に力を入れました。複雑な調査であり、大部分が手つかずの状態だったからです。今回の調査結果で明らかになった脆弱性については、すでに数えきれないほどの投稿があります。たとえば、 srvsvc や Wininit.exeなどはご存じのことでしょう。今回の調査では膨大な量のデータやツールが収集されました。当然、これらは 1 か所に集約しなければなりません。その場所が、Akamai の RPC ツールキットです。
このオープン・ソース・リポジトリには、RPC への移行に着手したり進めたりするうえで必要なものがすべて含まれています。ツール、記事、ブログ投稿、会議だけでなく、RPC の脆弱性に関する情報やエクスプロイトの概念実証などもあります。このリポジトリは、防御者と調査者の両方が RPC の情報にアクセスできるようにするために構築されました。関係者全員が連携することで、セキュリティをさらに強化できると考えています。当社の取り組みに関する記事をお読みいただいた方々、ツールをご利用いただいた方々、これらを共有していただいた方々に感謝申し上げます。是非ご活用いただけると幸いです。
このツールキットの中には RPC Interface Analyzer があります。このツールを使用すると、脆弱な可能性があるインターフェースを迅速かつ簡単に特定できます。今回のブログでは、このツールの目的や調査結果だけでなく、RPC の概要やセキュリティメカニズムも紹介しますので、まだこのツールを知らない方々も是非お読みください。
RPC とは?そして、そのセキュリティメカニズムとは?
RPC(遠隔手続き呼び出し)はプロセス間通信(IPC)の一種であり、クライアントが、RPC サーバーで公開されているプロシージャを呼び出すことができます。クライアントは、通常のプロシージャコールと同じように関数を呼び出すことができるため、リモートインタラクションの詳細をコード化する必要は(ほとんど)ありません。サーバーは、同じマシンまたはリモートマシン上に別のプロセスでホストできます。
MS-RPC は、Microsoft が実装する RPC です。タスクのスケジュール設定、サービス作成、プリンターおよび共有の設定、リモートで保存される暗号化データの管理など、Windows のさまざまなサービスで頻繁に使用されています。広範な用途と RPC が持つベクトルとしてのリモート性。これが、今回の紹介に至った経緯であり、RPC の調査に多くのリソースを投入した背景です。多くの機能を備えているため、セキュリティの観点から大きな注目を集めています。
次のセクションでは RPC セキュリティコールバック について解説し、自動化を活用して、セキュリティや脆弱性の調査に向けた分析やそれらの調査の新たなリード生成を行う方法を紹介します。
RPC セキュリティコールバックとは?その仕組みは?
簡単に言うと、セキュリティコールバックは、RPC インターフェースを保護する方法の 1 つです。RPC サーバー開発者が実装するカスタムコールバックです。ロジックは開発者が決定します。開発者は、ユーザーベースのアクセス制御、認証、トランスポートタイプを適用したり、サーバーで公開されている特定の機能へのアクセスを禁止したりすることができます。
最終的に、コールバックは RPC_S_OK を返してクライアントとサーバーとの通信を許可するか、 RPC エラーコード(RPC_S_ACCESS_DENIED など)で拒否します。
クライアント要求の受け入れ可否は、属性(複数の場合あり)に依存するのが一般的です。属性については、以下で説明します。
プロトコルシーケンス
クライアントは、複数のトランスポートを介してサーバーと通信できます。これらには、TCP、名前付きパイプ、ALPC などがあります。セキュリティコールバックでは、属性を確認して、ローカル接続要求のみ、リモート要求のみ、TCP 通信などでフィルタリングすることができます。
プロトコルシーケンスの値は文書化されていませんが、RpcCallAttributes 構造体のセキュリティコールバックに渡されるプロトコルシーケンスを次の値にマッピングしました。
#define ncacn_ip_tcp 1
#define ncacn_np 2
#define ncalrpc 3
#define ncacn_http 4
他のプロトコルシーケンス(ncacn_hvsocket など)は、 文字列バインディングの解析を通じてセキュリティコールバックによってテストできます。
認証レベル
セキュリティコールバックにおいて、クライアントの 認証レベル の確認は非常に一般的です。これにより、サーバーはクライアントに要求する最小認証レベルを定義できます。
認証レベルは次のように複数あります。それぞれは前のレベルを拡張するものになります。
#define RPC_C_AUTHN_LEVEL_DEFAULT 0
#define RPC_C_AUTHN_LEVEL_NONE 1
#define RPC_C_AUTHN_LEVEL_CONNECT 2
#define RPC_C_AUTHN_LEVEL_CALL 3
#define RPC_C_AUTHN_LEVEL_PKT 4
#define RPC_C_AUTHN_LEVEL_PKT_INTEGRITY 5
#define RPC_C_AUTHN_LEVEL_PKT_PRIVACY 6
たとえば、サーバーが RPC_C_AUTHN_LEVEL_PKT_PRIVACY の認証レベルを要求する場合、通信データはクライアントとサーバーにのみ表示されることになります。あるいは、 RPC_C_AUTHN_LEVEL_NONE の場合は、認証がないことを示します。
認証サービス
認証サービス は、認証レベルによって提示される認証ポリシーの検証を担うサービスを規定しています。
認証サービスの定数は次のとおりです。
#define RPC_C_AUTHN_NONE 0
#define RPC_C_AUTHN_DCE_PRIVATE 1
#define RPC_C_AUTHN_DCE_PUBLIC 2
#define RPC_C_AUTHN_DEC_PUBLIC 4
#define RPC_C_AUTHN_GSS_NEGOTIATE 9
#define RPC_C_AUTHN_WINNT 10
#define RPC_C_AUTHN_GSS_SCHANNEL 14
#define RPC_C_AUTHN_GSS_KERBEROS 16
#define RPC_C_AUTHN_DPA 17
#define RPC_C_AUTHN_MSN 18
#define RPC_C_AUTHN_DIGEST 21
#define RPC_C_AUTHN_NEGO_EXTENDER 30
#define RPC_C_NETLOGON 68 (文書化されていません)
#define RPC_C_AUTHN_MQ 100
#define RPC_C_AUTHN_DEFAULT 0xffffffff
RPC_C_AUTHN_NONEの場合、認証がオフになります。一方、 RPC_C_AUTHN_WINNT の場合、NTLM プロトコルが使用されます。
認証サービスと値の完全なリストについては、 GitHub ページをご覧ください。
NULL セッション
NULL セッションは匿名接続です。この場合、クライアントは認証なしでサーバーと通信します。ユーザー名やパスワードもありません。
一般的に、セキュリティコールバックが登録されている場合は、サーバー登録時に「RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH」のフラグが指定されていない限り、デフォルトで NULL セッションはブロックされます(他のケースについては こちらを参照)。セキュリティコールバックでは NULLL セッションも確認できます。
これらのセッションへのアクセスをブロックすることで、セキュリティコールバックは RPC インターフェースを非認証ユーザーから保護します。
操作番号(opnum)
opnum は、クライアントが実行を要求するインターフェースの関数を表します。より正確に言うと、RPC サーバーの関数テーブルのインデックスです。
opnum の値を確認することで、サーバーはクライアントのアクセスを、リモートクライアントの機微な情報を処理する関数、カーネルメモリにアクセスする関数、ユーザーモードクライアントの関数など、特定の関数に制限したりブロックしたりすることができます
Akamai Security Research では、この確認に依存する セキュリティコールバックの興味深い例 をブログで紹介しています。
他のセキュリティコールバック確認は次のとおりです。
呼び出し元 - 呼び出し元がユーザーモードかカーネルモードかを確認
クライアント PID - 特定のプロセスのみを許可
文字列バインディング - プロトコルシーケンス、ネットワークアドレス、エンドポイント情報などの RPC 接続属性を検証
なりすまし - サーバーがクライアントのセキュリティコンテキストでコードを実行できることを確認
複合的な確認もあります。たとえば、lsasrv.dll の LsaCapRpcIfCallbackFn コールバックで、認証サービスが netlogon の場合、認証レベルは RPC_C_AUTHN_LEVEL_PKT_INTEGRITY より小さくする必要があります。
RPC Interface Analyzer:実践的な概要説明
RPC Interface Analyzer は、RPC インターフェースを調査するための自動化ツールです。これによって調査者は、2 つの異なるソース(インターフェース定義(IDL)ファイルまたは PE ファイル)から RPC インターフェースを検索したり分析したりできます。
IDL ファイル
IDL ファイル は、RPC インターフェースとその関数を定義するファイルです。一般利用可能な IDL ファイルを分析することで、RPC インターフェースに関する情報とそのサーバーが公開している関数を、パラメーターや戻り値の型と併せて取得できます。この情報を使用して、調査者は潜在的に脆弱な関数を探すことができます。たとえば、 PetitPotamなどでパス引数を受け取る関数が挙げられます。
当社の IDL アナライザーを実行する場合は、以下のコマンドを実行します。
1.idl_scraper スクリプトを使用して、Microsoft の Web サイトから IDL ファイルをすべてコンピューターにダウンロードします。
idl_scraper.py [-h] [-o OUTPUT] [-p PROTOCOL]
2.次に、idl_parser を実行して、IDL ファイルを解析します。
idl_parser.py [-h] [-r] input_path [output_path]
すると、RPC インターフェース名、汎用一意識別子(UUID)、公開されている関数名、署名を含む CSV ファイルが出力されます。
PE ファイル
IDL ファイルの解析は便利ですが、公開されている IDL ファイルにしかアクセスできないため、RPC インターフェースが一部欠落する場合があります。他の方法としては、ローカル・ファイル・システム上の RPC インターフェースを PE ファイル(.exe または.dll ファイル)で検索することもできます。これはライブプロセスを確認するよりも望ましい方法と言えます。この方法であれば、作動していない RPC サーバーや、保護されたプロセスで実行されている RPC サーバーを見落とすことはありません。
RPC PE Analyzer は、 RpcServerRegisterIf 関数(およびそのバリアント)により登録された RPC インターフェースを検索し、渡された引数を解析します。これは、逆アセンブラが提供されている場合に該当します。この関数を使用せずに実行すると、デフォルトモードでは regex を使用して RPC インターフェースを検出します。この 講演 では、検索プロセスについて解説しています。
RPC PE Analyzer をデフォルトモードで使用する場合、次のコマンドを実行します。
pe_rpc_scraper.py <scrape_path> <output_path>
このコマンドでは、インターフェース UUID、ロール(クライアント/サーバー)、関数名とアドレスなどの基本的な出力が得られます。
より詳細な情報が必要な場合は、スクリプトに逆アセンブラとパス(デフォルトではない場合)を追加できます。
pe_rpc_scraper.py [-d {idapro,radare}] [-P DISASSEMBLER_PATH] <scrape_path> <output_path>
逆アセンブラオプションを使用すると、次のインターフェース登録情報も加わります。
サーバー登録で提供されるフラグ
インターフェースのセキュリティコールバック名とアドレス
RPC サーバーのセキュリティ記述子(存在する場合)
グローバルキャッシング がセキュリティコールバックに対して有効かどうか
今回のブログと併せて公開された最新機能は、セキュリティコールバック解析も提供します。
使用例
使用可能な RPC インターフェースをコンピューターですべてスキャンするとします。RPC PE Analyzer を実行し、 C:\Windows\System32 のコピーを scrape_path として提供し、出力を繰り返します。
pe_rpc_scraper.py -d idapro “C:\Users\User\Documents\System32_Copy”
出力は JSON 形式のため、反復作業を行って特定の情報を検索するのは難しくありません。例:
DLL ファイル内の RPC クライアント/サーバーをすべて検索
コンピューター上の RPC クライアント/サーバーをすべて検索
特定の RPC インターフェースのクライアントを検索
特定の RPC サーバーの RPC セキュリティコールバックを検索
インターフェースレベルでキャッシングする RPC インターフェースを検索(このタイプのキャッシングが問題になる理由について、詳細はこちらの ブログ記事 を参照してください)。
これらは一部にすぎず、出力の用途は数多くあります。当社では、さまざまな用途やアイデアに関する声をお待ちしています。
新機能-セキュリティコールバック情報
RpcCallAttributes 構造体
RPC_CALL_ATTRIBUTES は、クライアント要求に関するデータを保持する構造体です。サーバー側のインターフェース・セキュリティ・コールバックでは、 RpcServerInqCallAttributes 関数を呼び出して情報を取得することができます。
typedef struct tagRPC_CALL_ATTRIBUTES_V3_W
{
unsigned int Version;
unsigned long Flags;
unsigned long ServerPrincipalNameBufferLength;
unsigned short *ServerPrincipalName;
unsigned long ClientPrincipalNameBufferLength;
unsigned short *ClientPrincipalName;
unsigned long AuthenticationLevel;
unsigned long AuthenticationService;
BOOL NullSession;
BOOL KernelModeCaller;
unsigned long ProtocolSequence;
RpcCallClientLocality IsClientLocal;
HANDLE ClientPID;
unsigned long CallStatus;
RpcCallType CallType;
RPC_CALL_LOCAL_ADDRESS_V1 *CallLocalAddress;
unsigned short OpNum;
UUID InterfaceUuid;
unsigned long ClientIdentifierBufferLength;
unsigned char *ClientIdentifier;
} RPC_CALL_ATTRIBUTES_V3_W;
セキュリティコールバックが実行するテストについて解説しました。これにより、値を個別に照会したり(たとえば、 RpcStringBindingParseW ではプロトコルシーケンスを取得でき、 RpcBindingInqAuthClient では認証情報を取得できるなど)、構造体を使用したりすることができます。構造体にはあらゆるデータが保持されており、また必要な関数呼び出しは 1 つだけです。実際、解析したほとんどのセキュリティコールバックでは RpcServerInqCallAttributes が呼び出され、RPC_CALL_ATTRIBUTES の構造体を使用してすべての属性を同時に照会します。セキュリティコールバックのロジックを理解したい方にとって、この構造体は非常に興味深いものと言えます。
この構造体には現在「1」「2」「3」の 3 つのバージョンがあります。いずれも旧バージョンを拡張したものであり、それぞれ ANSI と Unicode のバージョンがあります。それぞれのバージョンとそのメンバについては、こちらの GitHub ページでご確認ください。
セキュリティコールバック情報
RPC ツールキットに新たに追加されたのは セキュリティコールバック情報であり、これは RPC PE Analyzerに含まれています。クライアントの要求を承認/拒否する前に、セキュリティコールバックが実行する確認や検証をピークできます。
RPC インターフェースのセキュリティコールバックや、特に RPC_CALL_ATTRIBUTES 構造体へのアクセスを解析すると、インターフェースに何らかの手掛かりが見つかる可能性があります。特定の認証プロトコルを使用する(または使用しない)RPC インターフェースをフィルタリングする場合は、このようにして 認証サービス の属性を確認するセキュリティコールバックを検索することができます。また、クライアント要求を許可したり、サーバー登録フラグで NULL セッションを許可したりする前に、 NULL セッション を確認しない RPC インターフェースを照会して、脆弱な可能性のある RPC インターフェースを検出することもできます。
その仕組みは?
各セキュリティコールバックに対して、アナライザーは次のように機能します。
使用されている RPC_CALL_ATTRIBUTES STRUCT のバージョンを検索し、IDA のローカルタイプに関連する構造体を定義
RpcCallAttribute のローカル変数を見つけ、そのタイプとして RPC_CALL_ATTRIBUTES 構造体を適用
この構造体を使用したセキュリティコールバックによる確認を解析して、テスト対象の要素、比較対象の値、使用する演算子(== / != / > / < / など)を出力します。
使用方法
使用方法は変わりません。RpcCallAttributes 構造体のメンバやテスト内容にアクセスする場合、IDA の逆アセンブラフラグを使用して RPC PE Analyzer を実行すると、各 RPC インターフェースの出力にセキュリティコールバック情報が毎回含まれるようになりました。
注:この情報の追加は、現在 IDA でのみ有効です。Radare のオプションを使用してアナライザーを実行しても、セキュリティコールバック情報は含まれません。
出力が得られたら、それを使用して脆弱性が存在している可能性のある RPC インターフェースを検索できます。必要に応じてフィルタリングすることもできます。以下に例を示します。
RPC_C_AUTHN_LEVEL_PKT_PRIVACY の認証レベルのみを要求する RPC インターフェースを取得する
ローカル接続を要求する RPC インターフェースを取得する
カーネルモードの呼び出し元のみを要求する RPC インターフェースを取得する
キャッシングの脆弱性の場合と同様に、opnum を確認する RPC インターフェースを取得する
まとめ
今、RPC は調査の基盤として最適であることが明らかになっています。長らく数多くの重要なプロセスに組み込まれていることを考えればなおさらです。当社がリポジトリにリソースをプールして、もうすぐ 1 年になります。RPC の脅威の可能性を十分に認識するために調査すべきことが山ほどあります。最近は注目されるようになりましたが、大部分が手つかずのままとなっているため、攻撃者の手口は無数にあり、まだまだ発見していかなければなりません。
RPC に備わる機能により、潜在的な危険性の調査をあらゆる角度から確実に行うことができます。今後も調査を続け、リポジトリのツールによってこのベクトルがさらに明らかになり、他の調査者の取り組みが促進されることを願っています。RPC のセキュリティ確保を担う防御者にとっても、次のターゲットを探す調査者にとっても、知識と洞察において RPC には将来性があります。