ポインタのリダイレクト

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

文字列ポインタ

関数呼び出しがstack上にある文字列のアドレスを使う場合、バッファオーバーフローを悪用してこのアドレスを書き換え、バイナリ内の別の文字列へのアドレスを入れることが可能です。

例えば、system 関数呼び出しがコマンドを実行するための文字列のアドレスを使う場合、攻撃者はスタック上に別の文字列のアドレス(例: export PATH=.:$PATH)を置き、カレントディレクトリに新しい文字列の最初の文字の名前を持つスクリプトを作成すれば、バイナリがそれを実行してしまいます。

実際のターゲットでは、単に表示されるテキストを変えるよりも、スタック上の文字列ポインタを再指向する方が通常はより興味深いです:

  • 後続の system/popen/execl* 引数を、既にメモリ上にある "/bin/sh" や攻撃者が制御するコマンド文字列へリダイレクトする。
  • 後続の読み込みシンク(例: puts("%s", ptr)write(fd, ptr, len))をリダイレクトしてスタック、heap、またはbinaryデータを leak する。
  • 後続の書き込みシンク(例: strcpy(dst, ...)memcpy(dst, src, len)、または ptr->field = value を介した構造体フィールド代入)をリダイレクトして、スタックオーバーフローを second-stage の arbitrary write に変える。

監査する際は、オーバーフローの後に関数が返る前に使用される char *cmdchar *pathchar *bufFILE *fp のようなスタックのローカルや、一時的な request/response 構造体内のポインタを優先して探してください。これは、オーバーフローが canary に到達できない場合や、近傍のポインタを壊すだけで十分な場合に特に有用です。

破壊が部分的な上書きに限定される場合(例えばバグが 0x00 を付加するため)、ポインタを次の先にリダイレクトすることを検討してください:

  • 同じ stack frame 内の近傍の文字列
  • 同じモジュール / non-PIE イメージ内の別のオブジェクト
  • 上位バイトが変わらないような制御可能な領域

トレーリング NUL が専用のローカル変数ではなく既存の stack pointer を変更するような ASLR 指向の関連ケースについては、Ret2ret & Reo2pop を参照してください。

次の例が参考になります:

関数ポインタ

文字列ポインタと同様に、stack に呼び出される関数のアドレスがある場合、それを変更して(例えば system を呼ぶように)利用することが可能です。

有用なターゲットは明示的なコールバック変数(例: void (*fp)())だけではありません。実際には次のような箇所を探してください:

  • 後にヘルパー関数に渡される local struct に格納された callbacks
  • エラーパスで呼ばれる destructor / cleanup handlers
  • parser dispatch tables や state-machine handlers が stack にコピーされる箇所
  • 後で間接呼び出しを通じて dispatch される local structs / objects

現代のエクスプロイトでは、pointer redirection は canary に触れる前に利用できる最後のプリミティブであることが多いです。CVE-2024-20017 に対する 2024 年のエクスプロイト解説では典型的なパターンが示されています: オーバーフローが stack のいくつかのローカル変数に達するが stack canary の手前で止まり、攻撃者は stack pointer とそれに関連する length/value を破壊し、そのポインタ経由の後続の代入が canary を壊すことなく arbitrary write になる、という流れです。

第二段階プリミティブへのポインタ破壊

近傍のポインタが後で store のために間接参照される場合、最初のオーバーフローで直接ジャンプすることを目指すのではなく、通常はプリミティブを「アップグレード」することを目標とします:

  1. ローカルバッファをオーバーフローさせ、ポインタと関連する length / integer / index を破壊する。
  2. 関数が ptr->len = xmemcpy(ptr, src, n)、または *ptr = value のような post-overflow の dereference を行うのを待つ。
  3. その結果得られる write-what-where を使って GOT スロット、コールバック、設定ポインタ、あるいは別の間接 callsite を上書きする。

これは次の場合に有効な選択肢です:

  • バグが canary で止まる場合
  • 関数ポインタ自体が直接到達不可能な場合
  • 4バイトまたは8バイトの data write の方が即座の control-flow hijack より容易な場合

同じアイデアは、破壊されたポインタが後でログ出力、印字、またはネットワーク送信ヘルパーに渡される場合の read プリミティブにも適用できます。

Modern AArch64 注記: PAC / BTI

現在の AArch64 ターゲットでは、古典的な saved return address の上書きは epilogue が x30 を PAC で認証するために失敗することがあります。そういったケースでは、ローカル関数ポインタやコールバックポインタのような non-return hijacks の方が魅力的になります。

しかし、BTI が有効な場合、上書きされた間接呼び出し先は有効な landing pad(通常は bti c を持つ関数エントリ、あるいは PAC が有効なコードでは paciasp/pacibsp で始まるプロローグ)に着地しなければなりません。したがって、AArch64 でスタック上の関数ポインタをリダイレクトする場合は次を優先してください:

  • 関数中間の gadget よりも実際の関数エントリ
  • プロローグが既に BTI を満たすターゲット
  • 間接呼び出しポインタが使用前に追加で認証されないターゲット

AArch64 のスタックオーバーフローに関する関連コンテキストは ret2win-arm64 を参照してください。

例は次のリポジトリにあります:

参考

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