
静的解析と動的解析(最初にお読みください)。 最初に、_動的_解析と_静的_解析の境界を明確にしておく価値があります。このツールはランキングを純粋に静的解析で行います。 コンパイル済みのMIDL/NDR構造をバイナリから直接読み取り、何も実行しません。
Windows RPC攻撃面の静的トリアージ。 PEバイナリのフォルダを指定すると、RPCサーバーを登録するすべてのバイナリを検出し、各インターフェースのNDRメソッドシグネチャ、トランスポートバインディング、登録フラグをコンパイル済みMIDL構造から直接抽出し、すべてのインターフェースを到達可能性 × 危険性でランク付けします。各スコアには完全な算術レシートが添付され、手計算で検証できます。
既存のRPCツールは、インターフェースのメソッドシグネチャと登録フラグを簡単に復元できます。しかし、どれも両方を取得して、実際にどこに時間を費やすかを決定する唯一の質問に答えることはできません。このインターフェースに到達できるとして、そのメソッドが入力として何を受け入れるかを考えると、マシン上の他のすべてと比較してどのくらい緊急に調査すべきか? このギャップを埋めるのがこのツールです。
フィルタリング: 対象フォルダをフィルタリングし、Ghidraが実際にRPCサーバーを登録するバイナリのみを自動解析するようにします(rpcrt4.dllをインポートし、RpcServerRegisterIf\* APIのいずれかを呼び出すバイナリ)。System32では、これが午後1回と1週間の違いになります。
抽出: インターフェースごとに、UUID、RPC_SERVER_INTERFACE / MIDL_SERVER_INFOチェーン、信頼できるDispatchTableCount、各メソッドのopnum + パラメータ方向 + デコードされたNDRオペコード、登録フラグ(R9)、セキュリティコールバック("bouncer")の有無、セキュリティ記述子(ベストエフォート)、エンドポイント/トランスポートバインディングを抽出します。
ランク付け: すべてのクリーンなインターフェースを2つの独立した軸で評価し、それらを乗算して0〜100の複合スコアにし、Critical / High / Moderate / Lowのバケットに分類します。
自己説明: 各スコアには、各コンポーネントと最終数値を生成した算術を列挙したレシート文字列が付属します。
target dir --(pefile filter) --> only RPC-registering PEs
--(Ghidra headless auto-analysis) --> analyzed program DB
--(extract_rpc_interfaces.py script) --> interfaces + NDR + flags + endpoints
--(two-axis AHP ranking engine) --> ranked interfaces + receipts
--> single JSON report
ゼロシンボル依存: このエンジンの重要な技術的差別化要因は、デバッグシンボルなしで完全に動作することです。ディスパッチテーブルをプログラム的にウォークし、生のNDR(Network Data Representation)バイトコードとコンパイル済みMIDL構造をメモリから直接解析することで、Microsoftの.pdbファイルやライブのエンドポイントマッパークエリを必要としません。これにより、エンジンは出荷時のままのストリップされた本番System32バイナリでそのまま動作することが保証されます。
Ghidra 11.x(同梱のsupport/analyzeHeadlessを使用)。PATHに**JDK 17+**が必要。
ドライバ側にPython 3.8+とpefile。
抽出スクリプトはGhidraに同梱のJython 2.7で実行されます。サードパーティのインポートは不要で、インストールもありません。
対象: Windows x64 PEファイル。解析自体はOSに依存しません(Ghidraはクロスプラットフォーム)ので、Windowsで実行する必要はありません。
git clone https://github.com/talha-nazeef-ahmed/RPC-Triage
cd RPC-Triage/
python -m pip install -r requirements.txt # just pefile
requirements.txt: pefile>=2023.2.7
すべてはorchestrator.pyによって駆動されます。フィルタリング、インポート、解析、抽出の実行を自動で行います。
python orchestrator.py \
-t \"C:\Windows\System32\" \
-g \"C:\ghidra_12.1.2_PUBLIC\" \
-s \".\extract_rpc_interfaces.py\" \
-o \".\out\report.json\" \
--stagedir \".\out\staged\" \
--projdir \".\out\ghidra_proj\" \
--projname RPC_Atlas
初回実行と再実行(重要)。 初回実行では遅い部分を行います。フィルタリング、一致するバイナリのインポート、完全な自動解析を実行し、--projdir内に.analysisCompleteマーカーを配置します。同じプロジェクトに対する以降の実行は、インポート/解析(-process -noanalysis)をスキップし、解析済みプログラムに対してスクリプトを再実行するだけです。したがって、System32の解析は一度だけのコストであり、出力の反復は安価です。解析が中断された場合、マーカーは書き込まれず、部分的なプロジェクト(.rep / .gpr)を削除して最初からやり直します。
1つのJSONファイル: バイナリのリストで、それぞれにInterfaces配列があります。System32全体の完全な実行結果は、このリポジトリのoutput/FullBatchRun.jsonに含まれています。これは生の、未編集の出力なので、ツールが大規模に何を生成するかを正確に確認できます。インターフェースごとに:
タグ: データ衛生ゲート
Clean: 正常に復元され、通常どおりランク付けされます。
Needs-Review: ランク付けされますが、ディスパッチテーブルのウォークが格納カウントを超えてしまいました(通常は末尾のサンクブロック)。スコアは本物ですが、[Provisional]が付きます。引用する前にメソッド数を検証してください。
Diagnostics / Diagnostics (2): この行は抽出アーティファクトです(UUIDとして誤ってラッチされたASCII文字列や破損したMIDLポインタなど)。スコアリングされません(Rank: N/A)。これらは意図的に保持されています。ツールの健全性を報告するものであり、攻撃面ではありません。
FLAG: Walked X != Stored Y について。 格納されているDispatchTableCountが信頼できるものであり、すべてのスコアがこれを基準にしています。ウォークされたカウントは独立した実行可能性のクロスチェックです。2つが一致しない場合(一般的にはきれいな2倍)、インターフェースはNeeds-Reviewとタグ付けされ、目視確認が必要であることがわかります。スコアを黙って変更することはありません。
これがスコアを議論可能にする部分です:
Moderate/35 | Gate:35 [ncacn_np:41, MultiEndpointBonus:15, HasBouncer:-46, BouncerIsNotCaching:25] | Surface:100 [HasBogusStruct:1x(opnums 5):[in]:61, HasCallerSizedBuffer:5x(opnums 0,6,11):[in]:49, InPtrs:6:18, Count:12:6 -> raw:134 [capped to 100] * 1.0 -> 100] | (35 * 100) / 100 = 35 [Provisional]
左から右に読みます:
Moderate/35: ティアと複合スコア。
Gate:35 [...]: 到達可能性の軸。トランスポートベース(ncacn_np:41、名前付きパイプ)から始まり、各登録修飾子とその符号付き寄与をリストします。MultiEndpointBonus:15(複数のトランスポートに登録)、HasBouncer:-46(セキュリティコールバックが存在し、到達可能性を下げる)、BouncerIsNotCaching:+25。合計して[5,100]にクランプ -> 35。
Surface:100 [...]: 危険性の軸。各発火シグナルはName:count x(opnums):direction:weight形式です。例: HasBogusStructは1つのパラメータ(opnums 5)で発火し、重みは61です。次にカウントベースの寄与: InPtrs:6:18 = 呼び出し元制御の入力ポインタ6つで+18、Count:12:6 = 12メソッドで+6。raw:134はキャップ前の合計、[capped to 100]、は信頼度乗数(シグネチャが不確かな場合は0.5に低下)。最終Surfaceは。
クランプと低信頼度ディスカウントを示す2つ目の例:
Low/3 | Gate:5 [Dynamic / epmapper:29, LocalCallOnly:-100, SecureOnly:-65, HasBouncer:-46, BouncerIsNotCaching:25] | Surface:50 [... -> raw:140 [capped to 100] * 0.5 -> 50] | (5 * 50) / 100 = 3
LocalCallOnly:-100だけでゲートがゼロ未満になるため、5のフロアにクランプされます。シグネチャが不確かだったためSurfaceは半分になり(* 0.5)、複合スコアは3になります。危険な入力空間ですが、実質的に到達不能 -> 正しく優先度が下げられています。
Critical >= 75、High >= 50、Moderate >= 25、それ以外はLow(回復されたサーフェスがゼロのインターフェースは、ゲートに関係なくLow)。しきい値は複合スコアに適用されます。その背後にあるモデルはdocs/Surface_Scoring_Methadology.mdにあります。
3つの重みテーブルすべて(トランスポートベース、ゲート修飾子、サーフェスシグナル)は、分析階層プロセス(AHP)で導出されています。ペアワイズ比較、幾何平均重み、測定された一貫性比を使用します。完全な導出、行列、一貫性の数値、手計算のメモはdocs/Surface_Scoring_Methadology.mdにあります。
抽出が単に自己確認的にならないよう、このツールが復元するインターフェースは、同じバイナリに対して実行された独立した確立されたRPC IDL抽出ツール(James ForshawのNtObjectManager内のRpcServerパーサー)と相互検証されています。lsass、samsrv、winlogonのリファレンスダンプはvalidation/にあり、完全なウォークスルーはvalidation/VALIDATION.mdにあります。いずれかをoutput/FullBatchRun.json内の対応するバイナリと比較してください。インターフェースUUID、メソッド/opnum数、パラメータごとの方向が一致します(たとえば、winlogonの12E65DD8-...インターフェースは、両方で5つのメソッド(Proc0-Proc4)を示しています)。リファレンスツールはIDLの復元で停止します。このツールは同じ復元されたサーフェスを取り、その上に到達可能性 × 危険性のランキングを追加します。そのプロジェクトとは提携していません。独立したグラウンドトゥルースチェックとしてのみ使用されています。
静的のみ。 何も実行されません。到達可能性は実行時ではなく登録から推測されます。
Needs-Reviewスコアは仮のものです。メソッド数を目視確認するまで有効です。
このツールは、ALPC/RPCとWindows内部に関する私の進行中の研究の一部です。近い将来、さらなる発見を公開し、関連ツールをリリースする予定です。これが役に立ったと思われた方は、GitHubまたは以下のソーシャルでフォローして、今後のリリースの通知を受け取ることを検討してください。
所有するシステム上の脆弱性調査のための静的トリアージ/マッピングツールです。攻撃_サーフェス_を報告するものであり、脆弱性そのものではありません。ハイライトされたインターフェースで見つけたものは、公開前に必ず調整された開示(MSRC)を通してください。
MIT
| flag | 意味 |
|---|
-t / --target | スキャンするバイナリのフォルダ |
-g / --ghidra | Ghidraインストールフォルダ(support/analyzeHeadlessを含む) |
-s / --script | extract_rpc_interfaces.pyへのパス |
-o / --output | 書き込むJSONレポートのパス |
--stagedir | フィルタリングされたRPCバイナリがコピーされるフォルダ(保持される) |
--projdir | 永続的なGhidraプロジェクト用フォルダ |
--projname | Ghidraプロジェクト名(例: RPC_Atlas) |
| field | 意味 |
|---|
CallSite | RpcServerRegisterIf\*呼び出しのアドレス |
Tag / TagDesc | データ品質バケット(下記参照) |
Rank | \"{Tier}/{Composite}\"、例: Critical/91 |
RankDetail | 完全なスコアリングレシート(下記参照) |
UUID | インターフェースUUID |
InterfaceAddress / DispatchAddress | 復元された構造アドレス |
FunctionsCount | 信頼できる格納メソッド数。(FLAG: Walked X != Stored Y)が付く場合あり |
Endpoints | トランスポート/エンドポイントバインディング |
Security | HasBouncer、SecurityDescriptor、SecureOnly、LocalCallOnly |
Methods | opnumごとのパラメータリストとデコードされたNDRオペコード |
* 1.0(35 * 100) / 100 = 35 [Provisional]: 複合 = Gate x Surface / 100。到達可能性と危険性は平均ではなく乗算されます。なぜなら、危険性は到達できた場合にのみ意味があるからです。触れることのできない最大に危険なインターフェースが上位に浮上してはいけません。最後の[Provisional]フラグは、動的メモリウォークが格納されたメソッド数とわずかに一致しなかったことを警告しており、人間が境界を検証する必要があることを意味します。