IO利用之FSOP
Last Update:
Page View: loading...
IO利用之FSOP
建议了解的前置知识:
1
2
3vtable劫持
FILE结构
fopen函数
提要:
通过前面对IO的学习,我们知道_IO_FILE结构会使用_chain域相互连接成一个链表:!
该链表的头部是由_IO_list_all进行管理的,调试的时候发现有点像头插法
我们使用fopen函数打开一个文件时会返回一个FILE类型的指针,这时候我们的一个FILE结构体已经悄悄挂进了我们上面所说的链表了,其中挂进链表的操作是由_IO_link_in函数执行的
1 | |
此时链表结构变为:

原理:
Wiki原话:
FSOP(File Stream Oriented Programming)就是通过劫持 _IO_list_all 的值来伪造链表和其中的 _IO_FILE 项,其中关键的函数为 _IO_flush_all_lockp,但是单纯的伪造只是构造了数据还需要某种方法进行触发。FSOP 选择的触发方法是调用 _IO_flush_all_lockp,这个函数会刷新 _IO_list_all 链表中所有项的文件流,相当于对每个 FILE 调用 fflush,也对应着会调用 _IO_FILE_plus.vtable 中的 _IO_overflow。
简单来说就是我们得伪造一个FILE结构然后修改_IO_list_all 指向我们伪造的FILE结构,然后去触发 _IO_flush_all_lockp函数
那为什么要这样干呢
看源码
_IO_flush_all_lockp函数:
1 | |
其中_IO_OVERFLOW看起来很熟悉,它是vtable中的函数,那么我们就可以通过触发 _IO_flush_all_lockp可以去劫持它为我们的gadget,但是有条件:当我们FILE结构输出缓冲区还有数据的话才会去调用 _IO_OVERFLOW函数
我们得绕过一些检查:
1 | |
fp->_mode <= 0
fp->_IO_write_ptr > fp->_IO_write_base 也就是 (fp->_IO_write_ptr)- (fp->_IO_write_base) >0
假设我们已经伪造了一个FILE结构 我们就可以将我们伪造的FILE结构中fp->_mode 设置为 0 ,fp->_IO_write_ptr设置为1,fp->_IO_write_base设置为0这样就可以绕过我们的检查
然后我们将_IO_OVERFLOW的vtable指针改为system,伪造fp的头部改为/bin/sh
回到_IO_flush_all_lockp函数的问题
那我们怎么触发 _IO_flush_all_lockp函数?
_IO_flush_all_lockp不需要我们手动调用,当程序退出的时候会自动调用此函数
触发_IO_flush_all_lockp时机:
- libc执行abort函数时。
- 程序执行exit函数时。
- 程序从main函数返回时。
当我们布置好这一切后,当程序退出时,执行abort函数中的_IO_flush_all_lockp然后调用 _IO_OVERFLOW的时候执行system(‘/bin/sh’)
这样就完成了我们的FSOP