IO leak
字数 1696 2025-08-05 11:39:48
IO File结构利用技术详解
背景知识
在堆利用场景中,当程序没有提供show函数且开启了FULL RELRO保护时,无法通过unsortedbin泄露基址。此时可以利用IO File结构进行基址泄露。
基本概念
文件描述符与FILE结构
- Linux初始文件描述符:0(stdin)、1(stdout)、2(stderr)
- FILE文件流描述文件,初始创建的三个文件(stdin/stdout/stderr)位于libc上
- 后续创建的FILE结构位于堆中,采用单向链表结构
_IO_FILE_plus结构
struct _IO_FILE_plus {
_IO_FILE file;
_IO_jump_t *vtable;
}
_IO_list_all:指向FILE文件链表头- 链表顺序:stderr → stdout → stdin
_IO_file_jumps结构
- 所有FILE文件共用的函数指针表
- 包含
__finish、__overflow、__underflow等函数指针 - 可通过修改这些指针或伪造vtable结构进行利用
泄露原理
通过篡改_IO_2_1_stdout_结构体中的字段:
- 修改
flags字段绕过检查 - 修改
_IO_write_base字段使write系统调用打印指定内存区域
puts函数调用链
puts → _IO_sputn → _IO_new_file_xsputn → _IO_overflow → _IO_new_file_overflow
关键检查绕过
-
_IO_new_file_overflow检查:f->_flags & _IO_NO_WRITES = 0f->_flags & _IO_CURRENTLY_PUTTING = 1
-
new_do_write检查:- 设置
fp->_flags | _IO_IS_APPENDING避免进入else if分支 fp->_IO_write_base不能大于fp->_IO_write_end
- 设置
常用payload构造
# 泄露_IO_file_jumps
payload = p64(0xfbad1800) + p64(0)*3 + b"\x58"
# 泄露_IO_2_1_stdin_
payload = p64(0xfbad3887) + p64(0)*3 + p8(0)
利用流程
- 申请
_IO_2_1_stdout_结构体 - 向结构体写入构造好的数据
- 执行puts函数泄露libc地址
不同libc版本的申请方式
- 2.27和2.31:利用tcache poisoning修改fd指针
- 2.23:fastbin size检查,只能伪造
malloc_hook-0x23或stdout结构体地址-0x43
爆破技巧
当ASLR开启时,stdout结构体地址后三位固定,倒数第四位随机。可通过爆破预测该位:
- 利用unsorted bin中的fd指针(指向main_arena+88/96)
- 修改fd指针为stdout结构体地址
- 爆破倒数第四位
高级利用技巧
realloc_hook调整栈帧
当只能劫持malloc_hook时,结合realloc_hook调整栈帧使onegadget生效:
malloc → malloc_hook → realloc → realloc_hook → onegadget
通过realloc_addr+offset调整push次数(offset可取0,2,4,6,11,12),抬高栈满足onegadget条件。
实战案例
de1ctf_2019_weapon (libc2.23)
漏洞点:UAF漏洞,无show函数
利用步骤:
- 扩展fastbin到unsorted bin
- 将unsorted bin链放入fastbin
- 修改main_arena+0x88为
&_IO_2_1_stdout_-0x43 - 构造payload泄露libc
- fastbin attack修改malloc_hook
关键payload:
add(0x60, 11, b'\x00'*0x33 + p64(0xfbad1887) + p64(0)*3 + b'\x00')
nsctf_online_2019_pwn1
漏洞点:负数溢出漏洞
利用步骤:
- 利用负数溢出找到
_IO_2_1_stdout_结构体指针 - 修改
_IO_write_base和_flags泄露libc - 伪造vtable,篡改
_flags、_lock、vtable、_IO_save_base字段 - 劫持
_IO_new_file_xsputn为system
关键payload:
pl = flat([
b'/bin/sh\x00',
p64(0)*8,
libc.symbols['system'] + libc_base, # _IO_save_base
p64(0)*6,
p64(0),
libc_base + 0x3c6780, # lock
p64(0),
p64(0)*8,
libc_base + libc.symbols['_IO_2_1_stdout_'] + 0x10 # vtable
])
总结
IO File结构利用是一种强大的技术,适用于无show函数和FULL RELRO保护场景。关键在于理解FILE结构布局和函数调用链,通过精心构造的payload可以绕过各种检查实现信息泄露和代码执行。