CTF における Format String Attack (pwntools を用いた攻撃)
Format String Attack (書式文字列攻撃) について CTF で出題されたとき用の覚え書き
そもそも Format String 脆弱性とは
- フォーマット指定子の仕様を悪用した攻撃
- 例として下記のように c 言語の
printfに入力文字列をそのまま与えている際に,%pなどを送り込むことでスタックのリークが可能
input = $INPUT printf(input) # input に %p などが含まれるとフォーマット指定子として認識されてしまう
よく使われるフォーマット指定子
%p: ポインタ,%lxと同じで 8 バイト分読みだす%sアドレスに対応するメモリについて, 文字列として読みだす%nアドレスに対し書き込みを行う特殊な指定子. 後述するpwntoolsでペイロードを作成可能実用的には,
%6$pのように番号を指定することで任意の場所の引数を指定することができる (%p %p %p %p %p %pのように何個も書く必要がなくなる)
pwntools を用いた書き換え用ペイロードの作成
fmtstr_payload関数を用いることでメモリ書き換えのペイロードを簡単に作成可能
fmtstr_payload($OFFSET, {$TARGET_ADDRESS: $VALUE}, numbwritten=0, write_size="byte")
$OFFSETには入力文字列が表示される引数の番号 (オフセット) を入れる
Format String Attack を用いた攻撃のシナリオ例
スタックのリーク
%pをいくつも置くことでスタックのリークが可能- スタック上 (前に宣言されている変数) にフラグにつながる鍵がある場合にリークして情報を読み取ることが可能
メモリ上の文字列の読み込み
- フラグが格納されているアドレスが分かる場合,
%sを用いることで読み出しが可能な場合がある - 具体的には, ペイロードの先頭にターゲットアドレスを置き, 入力文字に対応する引数について
%sを呼び出すことで読み出す
GOT Overwrite
- GOT (Global Offset Table) と呼ばれる, c 言語の基本的な関数の共有ライブラリのアドレスを保持するテーブルを書き換えることで, 任意の関数の呼び出しを可能にする
- pwntools ではバイナリがあれば
elf.gotで対象関数 (putsなど) を指定することで書き換えるべきアドレスを調べることができる
elf = ELF($FILE) got_address = elf.got[$NAME]
注意事項 (ミスの事例)
- ペイロードにヌルバイトが含まれるとうまくいかない場合がある
- 事前にバイナリに
checksecを掛けることでアドレスの相対化 (PIE), GOT Overwrite が可能かどうか (RELRO が partial か無しであれば可能) を調べておく - バイナリが 32 bit か 64 bit かに注意
- pwntools を使うときは以下のようにバイナリを指定しておくとエラーが起きづらい
context.binary = $FILE_NAME