FILE Exploration
系统地学一下 glibc 文件结构的洞
FILE 结构
1 | struct _IO_FILE { |
- _IO_FILE
- _flags
- 记录文件流的属性
- Read only
- Append
- …
- 记录文件流的属性
- Stream buffer
- Read buffer
- _IO_read_ptr
- _IO_read_end
- _IO_read_base
- Write buffer
- _IO_write_ptr
- _IO_write_end
- _IO_write_base
- Reserve buffer
- _IO_buf_base
- _IO_buf_end
- Read buffer
- _fileno
- 文件描述符
- _chain
- FILE 结构体是一个尾插法单向链表,默认有 stderr -> stdout -> stdin
- _lock
- 避免多线程的条件竞争
- 在攻击时通常需要构造它
- 使其指向一个全是0的空间
- _flags
- _IO_FILE_plus
- stdin/stdout/stderr/fopen 使用这个结构体
- _IO_FILE
- vtable
- 所有对文件的操作都是通过 vtable
fopen 流程
- 分配 FILE 结构体空间
- malloc
- 初始化 FILE 结构体
- _IO_new_file_init_internal
- 把 FILE 结构体放入链表
- _IO_link_in
- 打开文件
- _IO_new_file_open
- sys_open
fread 流程
- 如果 stream buffer 是空的
- vtable -> _IO_file_xsgetn
- 分配 buffer
- vtable -> _IO_file_doallocate
- 读取数据到 stream buffer 中
- vtable -> _IO_file_underflow
- 把数据从 stream buffer 复制到目的地址
- sys_read
fwrite 流程
- 如果 steam buffer 是空的
- vtable -> _IO_file_xsputn
- 分配 buffer
- vtable -> _IO_file_doallocate
- 复制用户数据到 stream buffer
- 如果 stream buffer 满了或者要刷新 steam buffer,将 steam buffer 的数据写入文件
- sys_write
fclose 流程
- 把 FILE 结构从链表中移除
- _IO_unlink_it
- 刷新并释放 stream buffer
- _IO_new_file_close_it
- _IO_do_flush
- 关闭文件
- sys_close
- 释放 FILE 结构
- vtable -> _IO_file_finish
- free
伪造 vtable
伪造 FILE 结构,将 vtable 指向构造的函数
- 修改 _lock 指向一个全为 0 的内存
- 找到 vtable 的偏移
- 修改 vtable 指向可控的内存
- 调试查看 close 时会 call 的位置和 rdi 参数
- 将对应位置改成 system 和 /bin/sh
注:一般 rdi 的值为 _flags + 后面四个字节,所以一般前 8 个字节设置为 AAAA;sh;
FSOP
File-Stream Oriented Programming
控制文件结构链表
- _chain
- _IO_list_all 全局变量,链表头
IO_flush_all_lockp
- 用于刷新所有 FILE 的缓存
- 调用条件
- 当 libc 执行 abort 时
- 当执行 exit 时
- 当从 main 返回时
- 在调用时,如果
fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base
会调用vtable->_IO_overflow
House of Orange
- 利用 Unsorted bin attack 把 unsorted bin 写到 _IO_list_all
- 构造 0x60 大小的 chunk 放入 small bin
- 调用 _IO_flush_all_lockp 有 50% 概率把 0x60 大小的 chunk 作为 FILE 结构造成 FSOP
Pwnable seethefile
1 | Arch: i386-32-little |
利用点
- 读取
/proc/self/maps
得到 libc 地址 - 在
case 5
的时候 name 溢出覆盖 fp 到 fake_file,fclose(fp)
时就可以使用伪造的 vtable
主要需要调试找到 _lock、vtable 和调用 vtable 中的函数的偏移
1 | from pwn import * |
FILE Exploration