アセットローダーにおける安全でないリロケーション修正

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をサポートする

なぜアセットのリロケーションが重要か

多くのレガシーゲームエンジン(Granny 3D、Gamebryo など)は複雑なアセットを次の手順でロードします:

  1. ヘッダーとセクションテーブルを解析する。
  2. セクションごとに1つのヒープバッファを割り当てる。
  3. 各セクションのベースポインタを格納する SectionArray を構築する。
  4. セクションデータ内に埋め込まれたポインタが正しいターゲットセクション+オフセットを指すように、リロケーションテーブルを適用する。

リロケーションハンドラが攻撃者制御のメタデータを盲目的に信頼すると、すべてのリロケーションが潜在的な arbitrary read/write primitive になります。Anno 1404: Venice では、granny2.dll が次のヘルパーを提供します:

`GrannyGRNFixUp_0`(省略) ```c int *__cdecl GrannyGRNFixUp_0(DWORD RelocationCount, Relocation *PointerFixupArray, int *SectionArray, char *destination) { while (RelocationCount--) { int target_base = SectionArray[PointerFixupArray->SectionNumber]; // unchecked index int *patch_site = (int *)(destination + PointerFixupArray->SectionOffset); // unchecked offset *patch_site = target_base ; if (target_base) *patch_site = target_base + PointerFixupArray->Offset; ++PointerFixupArray; } return SectionArray; } ```

SectionNumber はレンジチェックされず、SectionOffset は現在のセクションサイズに対して検証されません。負のオフセットや過大なインデックスを持つリロケーションエントリを作成すると、制御しているセクションの外へ移動して、セクションポインタ配列のようなアロケータのメタデータを上書きできます。

Stage 1 – ローダーのメタデータへ逆方向に書き込む

目的は、section 0 のリロケーションテーブルにより SectionContentArraySectionArray をミラーし、最初のセクションバッファの直前に格納される)のエントリを上書きさせることです。Granny のカスタムアロケータが先頭に 0x1F バイトを付加し、NT heap が独自の 0x10 バイトヘッダとアラインメントを追加するため、攻撃者は最初のセクションの開始位置(destination)とセクションポインタ配列との距離を事前に算出できます。

テストしたビルドでは、ローダに正確に 0x4000 bytesGrannyFile 構造を割り当てさせると、セクションポインタ配列が最初のセクションバッファの直前に配置されます。これにより

0x20 (header) + 0x20 (section descriptors)
+ n * 1 (section types) + n * 1 (flags)
+ n * 4 (pointer table) = 0x4000

これにより n = 2720 のセクションが生成される。SectionOffset = -0x3FF00x4000 - 0x20 - 0x20 + 0x30)のリロケーションエントリは、送信先セクションが内部ポインタをパッチしていると思っていても、SectionContentArray[1] に解決されるようになる。

Stage 2 – Deterministic heap layout on Windows 10

Windows 10 の NT Heap は、割り当てサイズが ≤ RtlpLargestLfhBlock (0x4000) のものをランダム化された LFH に振り分け、それより大きいものを決定論的なバックエンドアロケータに送る。GrannyFile のメタデータをその閾値をわずかに超えるように保持し(2720 セクショントリックを使用)、悪意ある複数の .gr2 アセットをプリロードすることで、次のようにできる:

  • Allocation #1 (metadata + section pointer arrays) が >0x4000 のバックエンドチャンクに配置される。
  • Allocation #2 (section 0 contents) が Allocation #1 の直後に配置される。
  • Allocation #3 (section 1 contents) が Allocation #2 の直後に配置され、その後のリロケーションに対して予測可能なターゲットを与える。

Process Monitor はアセットがオンデマンドでストリーミングされることを確認したため、実行可能ファイル本体に触れることなく、作成したユニット/建物を繰り返し要求するだけでヒープレイアウトを“プライム”するのに十分である。

Stage 3 – Converting the primitive into RCE

  1. Corrupt SectionContentArray[1]. Section 0 のリロケーションテーブルが -0x3FF0 オフセットを使ってこれを上書きする。これを任意の書き込み可能な領域(例: 後続のセクションデータ)に向ける。
  2. Recycle the corrupted pointer. Section 1 のリロケーションテーブルは今や SectionNumber = 1 をあなたが注入した任意のポインタとして扱う。ハンドラは SectionArray[1] + Offsetdestination + SectionOffset に書き込み、各リロケーションエントリごとに任意の4バイト書き込みを与える。
  3. Hit reliable dispatchers. Anno 1404 では、標的として選ばれたのは granny2.dll のアロケータコールバック(ASLR なし、DEP 無効)だった。granny2.dll が次の Malloc/Free 呼び出しで使用する関数ポインタを上書きすると、実行はトロイ化されたアセットからロードされた攻撃者制御下のコードに即座に逸らされる。

granny2.dll と注入された .gr2 バッファは ASLR/DEP が無効のときに安定したアドレスに存在するため、攻撃は小さな ROP チェーンまたは生のシェルコードを作成し、コールバックをそれに向けることに帰着する。

Practical checklist

  • SectionArray / relocation tables を維持するアセットローダーを探す。
  • インデックス/オフセットの境界チェックが欠けているかどうか、リロケーションハンドラを差分解析する。
  • ゲームのアロケータラッパーと基盤となる OS ヒープの双方が追加するアロケータヘッダを計測し、逆方向オフセットを正確に算出する。
  • 決定論的な配置を強制する方法:
    • メタデータを膨らませる(多数の空セクション)ことで割り当てサイズが RtlpLargestLfhBlock を超えるまで増やす。
    • 悪意あるアセットを繰り返しロードしてバックエンドの隙間を埋める。
  • 二段階のリロケーションテーブルを使用する(最初に SectionArray を再ターゲットし、次に書き込みをスプレーする)と、通常のレンダリング中に発火する関数ポインタ(allocator callbacks、virtual tables、animation dispatchers など)を上書きする。

マルチプレイヤーのセーブ転送におけるパストラバーサルなどで任意のファイル書き込みを得たら、加工した .gr2 を入れた RDA アーカイブを再パッケージすることで、リモートクライアントが自動的に展開するクリーンな配布ベクタが得られる。

References

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をサポートする