IO_FILE源码分析:FSOP
vtable
IO_FILE结构体里面有个很重要的数据结构 —— vtable
vtable 采用虚表调用的形式,如果劫持了 vtable ,就可以调用我们需要的任意函数,甚至可以伪造 vtable ,使程序错误使用我们构造的 fake vtable
1 | pwndbg> p/x*(struct _IO_FILE_plus*)_IO_list_all |
1 | pwndbg> p*(struct _IO_jump_t*)_IO_list_all.vtable // vtable的所有字段都可以劫持 |
下面是 raycp 大佬对 vtable 调用的总结
fread 函数中调用的 vtable 函数有:
_IO_sgetn函数调用了vtable的_IO_file_xsget_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 函数调用的 vtable 函数有:
_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 函数调用的 vtable 函数有:
- 在清空缓冲区的
_IO_do_write函数中会调用vtable中的函数 - 关闭文件描述符
_IO_SYSCLOSE函数为vtable中的__close函数 _IO_FINISH函数为vtable中的__finish函数
FSOP
终于到今天的主角了,FSOP全称是 File Stream Oriented Programming ,它的核心就在于伪造 _IO_list_all 指针( _IO_list_all 永远指向当前FILE结构体链的第一个元素)
- 这个技术的核心就是劫持
_IO_list_all的值来伪造链表和其中的_IO_FILE项,但是单纯的伪造只是构造了数据还需要某种方法进行触发 - FSOP 选择的触发方法是调用
_IO_flush_all_lockp,这个函数会刷新_IO_list_all链表中所有项的文件流(相当于对每个FILE调用 fflush),也对应着会调用_IO_FILE_plus.vtable中的IO_overflow函数
1 | _IO_flush_all_lockp (int do_lock) |
IO_flush_all_lockp 函数触发条件:
- 当 libc 执行 abort 流程时,abort 可以通过触发 malloc_printerr 来触发
- 当执行 exit 函数时
- 当执行流从 main 函数返回时
FSOP 攻击的前提条件:
- 泄露出 libc 地址,知道
_IO_lsit_all的地址 - 任意地址写的能力,修改
_IO_list_all为可控的地址 - 可以在可控内存中伪造
_IO_FILE_plus结构
伪造的 _IO_FILE_plus 结构体要绕过的 check:
1 | 1.((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base) |
FSOP 利用姿势
在考虑进行 FSOP 攻击时,常常不会有可控的 WAA,这时我们利用 unsortedbin attack 在 _IO_list_all 中写入 main_arena + 88 的地址,那么程序就会把 main_arena + 88 当做FILE结构体
然后 main_arena + 0xc0(88+0x68) 的位置就是FILE结构体的 _chain 条目,同时,main_arena + 0xc0 也会指向对应大小为“0x60”的 smallbin,那么大小为“0x60”的smallbin就会被程序当成下一个FILE结构体
到了这里通常有两种应对方式,一是直接申请大小为“0x60”的chunk,把它放入对应 smallbin 的头部,然后在其内部进行伪造,二是通过堆溢出等方式,修改 unsortedbin 的size为“0x61”(注意规避合并机制),再次申请 chunk,该 unsortedbin 就会被放入 smallbin
接下来就是对 FILE 结构体的伪造,只需要伪造 vtable 的指向地址,然后再该地址处写入 fake vtable 就可以了
而在对 vtable 的伪造中,主要是伪造 _IO_OVERFLOW 条目(第4个),因为程序对FILE结构的破坏,导致程序会触发报错提示,根据程序调用链发现中途会调用 _IO_OVERFLOW ,正好可以被控制
这就是 FSOP 的全过程了,可以去学习 house of orange 进行巩固
文字描述有点多,需要根据题目过一遍