IO_FILE-FSOP
August 2, 2019 PWN 访问: 43 次
由于刚开始接触IO_FILE攻击,利用的是glibc-2.23版本的,没有vtable check
机制
数据类型介绍
经过对fopen内部源码的调试和分析,知道了在_IO_FILE_plus
结构体中有一个vtable的字段,
_IO_FILE_plus
的定义:
struct _IO_FILE_plus
{
_IO_FILE file;
const struct _IO_jump_t *vtable;
};
如下所示:
$21 = {
file = {
_flags = -72540024,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x0,
_fileno = 0,
_flags2 = 0,
_old_offset = -1,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x7ffff7dd3790 <_IO_stdfile_0_lock>,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x7ffff7dd19c0 <_IO_wide_data_0>,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
}
vtable
指向的也是一个结构体(_IO_jump_t
):
struct _IO_jump_t
{
JUMP_FIELD(size_t, __dummy);
JUMP_FIELD(size_t, __dummy2);
JUMP_FIELD(_IO_finish_t, __finish);
JUMP_FIELD(_IO_overflow_t, __overflow);
JUMP_FIELD(_IO_underflow_t, __underflow);
JUMP_FIELD(_IO_underflow_t, __uflow);
JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
/* showmany */
JUMP_FIELD(_IO_xsputn_t, __xsputn);
JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
JUMP_FIELD(_IO_seekoff_t, __seekoff);
JUMP_FIELD(_IO_seekpos_t, __seekpos);
JUMP_FIELD(_IO_setbuf_t, __setbuf);
JUMP_FIELD(_IO_sync_t, __sync);
JUMP_FIELD(_IO_doallocate_t, __doallocate);
JUMP_FIELD(_IO_read_t, __read);
JUMP_FIELD(_IO_write_t, __write);
JUMP_FIELD(_IO_seek_t, __seek);
JUMP_FIELD(_IO_close_t, __close);
JUMP_FIELD(_IO_stat_t, __stat);
JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
JUMP_FIELD(_IO_imbue_t, __imbue);
#if 0
get_column;
set_column;
#endif
};
如下所示:
pwndbg> p *(struct _IO_jump_t *)0x7ffff7dd06e0
$22 = {
__dummy = 0,
__dummy2 = 0,
__finish = 0x7ffff7a869c0 <_IO_new_file_finish>,
__overflow = 0x7ffff7a87730 <_IO_new_file_overflow>,
__underflow = 0x7ffff7a874a0 <_IO_new_file_underflow>,
__uflow = 0x7ffff7a88600 <__GI__IO_default_uflow>,
__pbackfail = 0x7ffff7a89980 <__GI__IO_default_pbackfail>,
__xsputn = 0x7ffff7a861e0 <_IO_new_file_xsputn>,
__xsgetn = 0x7ffff7a85ec0 <__GI__IO_file_xsgetn>,
__seekoff = 0x7ffff7a854c0 <_IO_new_file_seekoff>,
__seekpos = 0x7ffff7a88a00 <_IO_default_seekpos>,
__setbuf = 0x7ffff7a85430 <_IO_new_file_setbuf>,
__sync = 0x7ffff7a85370 <_IO_new_file_sync>,
__doallocate = 0x7ffff7a7a180 <__GI__IO_file_doallocate>,
__read = 0x7ffff7a861a0 <__GI__IO_file_read>,
__write = 0x7ffff7a85b70 <_IO_new_file_write>,
__seek = 0x7ffff7a85970 <__GI__IO_file_seek>,
__close = 0x7ffff7a85340 <__GI__IO_file_close>,
__stat = 0x7ffff7a85b60 <__GI__IO_file_stat>,
__showmanyc = 0x7ffff7a89af0 <_IO_default_showmanyc>,
__imbue = 0x7ffff7a89b00 <_IO_default_imbue>
}
pwndbg>
这个结构体中存着和IO相关的函数,是一个函数表,而这个vtable
就是一个函数表指针。
这个函数表中有19个函数指针,分别完成IO的相关操作
比如说,fwrite最终会调用__write = 0x7ffff7a85b70 <_IO_new_file_write>,
劫持原理
如果可以控制_IO_FILE_plus
结构体中的vtable
指针,让这个指针指向我们伪造的_IO_jump_t
结构体,再通过调用相应的IO函数,就可以触发对我们伪造的_IO_jump_t
结构体中的一个函数的调用,就可以劫持程序流。
两种方法
- 修改内存中已有的
_IO_FILE_plus
结构体中的vtable
指针 - 修改并伪造整个
_IO_FILE_plus
结构体
例子
写个小demo:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void pwn()
{
printf("%s","hack by pwn!");
}
struct fake_vtable{
char *a0;
char *a1;
char *a2;
char *a3;
char *a4;
char *a5;
char *a6;
char *a7;
char *a8;
char *a9;
char *a10;
char *a11;
char *a12;
char *a13;
char *a14;
char *a15;
char *a16;
char *a17;
char *a18;
char *a19;
char *a20;
char *a21;
char *a22;
char *a23;
};
int main()
{
struct fake_vtable a;
// a.a7 = pwn;
a.a8 = pwn;
long long *vtable_addr;
FILE *file_point = fopen("test.txt","r");
char *content;
vtable_addr = (long long *)((long long)file_point+0xd8);
*vtable_addr=&a;
fwrite(content,1,8,file_point)
// fread(content,1,8,file_point);
return 0;
}
上面程序的输出:
一筐萝卜➜ test ./demo1
hack by pwn!# 一筐萝卜➜ test
经过测试后发现,调用fwrite
时会调用vtable
表中的第9个函数,调用fread
时会调用vtable
表中的第8个函数
IO_FILE 相关的操作对应的vtable表中的函数
首先要明白一点,fopen函数实在分配空间,建立_IO_FILE_plus
结构体,并且将结构体插入到_IO_list_all
中
function name | call function in vtable (下标从0开始,相对于结构体的开头) |
---|---|
fread | __GI__IO_file_xsgetn (8)、_IO_file_doallocate (13)、__GI__IO_file_stat (18)、_IO_new_file_underflow (4)、__GI__IO_file_read (14) |
fwrite | _IO_new_file_xsputn (7)、_IO_new_file_overflow (3)、_IO_file_doallocate (13)、__GI__IO_file_stat (18)、_IO_new_file_write (15) |
fclose | __GI__IO_file_close (17)、_IO_new_file_finish (2) |
攻击原理
在一个程序进程中,所有打开的文件结构都是由一个单链表(_IO_list_all
)来管理的,而每个结构体中指向下一个节点是_chain
字段,
pwndbg> print _IO_list_all
$6 = (struct _IO_FILE_plus *) 0x602010 #自己分配的FILE
pwndbg> print _IO_list_all->file._chain
$7 = (struct _IO_FILE *) 0x7ffff7dd2540 <_IO_2_1_stderr_>
pwndbg> print (struct _IO_FILE *)(_IO_list_all->file._chain)._chain
$8 = (struct _IO_FILE *) 0x7ffff7dd2620 <_IO_2_1_stdout_>
pwndbg> print (struct _IO_FILE *)((_IO_list_all->file._chain)._chain)._chain
$9 = (struct _IO_FILE *) 0x7ffff7dd18e0 <_IO_2_1_stdin_>
待续……