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 进行巩固
文字描述有点多,需要根据题目过一遍