FSOP
CFF-WIKI中有如下描述
-
FSOP 的核心思想就是劫持_IO_list_all 的值来伪造链表和其中的_IO_FILE 项,但是单纯的伪造只是构造了数据还需要某种方法进行触发。FSOP 选择的触发方法是调用_IO_flush_all_lockp,这个函数会刷新_IO_list_all 链表中所有项的文件流,相当于对每个 FILE 调用 fflush,也对应着会调用_IO_FILE_plus.vtable 中的_IO_overflow。
-
触发条件
- 梳理一下 FSOP 利用的条件,首先需要攻击者获知 libc.so 基址,因为_IO_list_all 是作为全局变量储存在 libc.so 中的,不泄漏 libc 基址就不能改写_IO_list_all。
- 之后需要用任意地址写把_IO_list_all 的内容改为指向我们可控内存的指针
- 之后的问题是在可控内存中布置什么数据,毫无疑问的是需要布置一个我们理想函数的 vtable 指针。但是为了能够让我们构造的 fake_FILE 能够正常工作,还需要布置一些其他数据。
-
具体操作
-
覆盖位于 libc 中的全局变量 _IO_list_all,把它指向我们伪造的_IO_FILE_plus。
通过调用 exit 函数,程序会执行 _IO_flush_all_lockp,经过 fflush 获取_IO_list_all 的值并取出作为_IO_FILE_plus 调用其中的_IO_overflow
-
-
如何调用
- 而_IO_flush_all_lockp 不需要攻击者手动调用,在一些情况下这个函数会被系统调用:
- 当 libc 执行 abort 流程时
- 当执行 exit 函数时
- 当执行流从 main 函数返回时
- 而_IO_flush_all_lockp 不需要攻击者手动调用,在一些情况下这个函数会被系统调用:
glibc 2.24 下 IO_FILE 的利用
_IO_str_jumps -> overflow原理
- 当程序执行exit函数,或者从main函数返回时,会执行调用_IO_flush_all_lockp,这个函数会刷新_IO_list_all 链表中所有项的文件流,相当于对每个 FILE 调用 fflush,也对应着会调用_IO_FILE_plus.vtable 中的_IO_overflow。
- 因而当设置stdout对应的_IO_FILE 对应的 vtable 为 _IO_str_jumps
- 执行exit就会执行,_IO_overflow
利用
-
伪造stdout -> _chain, 在其中布置,buf_base 、buf_end 、 vtable
_flags = 0 _IO_write_base = 0 _IO_write_ptr = (binsh_in_libc_addr -100) / 2 +1 _IO_buf_end = (binsh_in_libc_addr -100) / 2 _freeres_list = 0x2 _freeres_buf = 0x3 _mode = -1 vtable = _IO_str_jumps - 0x18
-
执行exit后会先后执行如下的操作
size_t old_blen = _IO_blen (fp); // #define _IO_blen (fp) ((fp)->_IO_buf_end - (fp)->_IO_buf_base) new_buf = malloc (new_size); memcpy (new_buf, old_buf, old_blen); free (old_buf);
总结
- 首先需要伪造stdout->_chain,这一步可以通过largebin attack实现。
- 然后在堆中伪造chain,触发malloc memcpy free三个操作
- 如果能够malloc到free_hook,然后将一个chunk中的内容,memcpy到free_hook,最后在执行free,就能获得shell。