house of apple 2
伪造_IO_FILE
结构体,伪造vtable
控制程序执行流。但_IO_FILE
中table
存在合法性检查,即取值只能为已有的几个虚表指针,因此直接伪造_IO_FILE
结构体中的vtable
为目的地址往往无法通过合法性检查。
_IO_FILE
中存在指向_IO_wide_data
结构体的指针,这个结构体专门用于处理宽字符。_IO_wide_data
结构与_IO_FILE
类似,也存在vtable
不过此处的vtable
没有合法性检查,因此可以把此处的vtable
为造成需要执行的目标地址,即可实现跳转
因此,实现house of apple 2
需要三块堆来实现攻击
- fake _IO_FILE
- fake _IO_wide_data
- fake _wide_vtable
不过部分题目能申请到足够大的堆也可以把这三个合并到一个堆来进行构造,只要满足攻击条件即可
下面三种路线均可实现,但只推荐第一种。后面两种所需的vtable无符号不方便寻找
_IO_wfile_overflow路线
_IO_wfile_jumps、_IO_wfile_jumps_mmap、_IO_wfile_jumps_maybe_mmap
虚表指针均会去调用_IO_wfile_overflow
下为_IO_wfile_overflow
攻击路线
- FILE->mode = 0 (_IO_flush_all_lockp 控制流判断条件)
- FILE->_IO_write_ptr > FILE->_IO_write_base (_IO_flush_all_lockp 控制流判断条件)
- FILE->_flags & 0x8 == 0 (_IO_wfile_overflow 控制流判断条件,注意_flags在FILE结构体最开头,与binsh字符串重合,因此不能直接写’/bin/sh’,本程序写入的是’ sh’)
- FILE->_flags & 0x800 == 0 (_IO_wfile_overflow 控制流判断条件)
- FILE->_wode_data->_IO_write_base == 0 (_IO_wfile_overflow 控制流判断条件,_IO_write_base偏移0x18)
- FILE->_wide_data->_IO_buf_base == 0 (_IO_wdoallocbuf 控制流判断条件)
- FILE->_flags & 2 != 0 (_IO_wdoallocbuf 控制流判断条件)
- FILE->_wide_data->_wide_vtable + 0x68 == 要执行的代码地址 (ALLOCATE函数指针偏移0x68)
c语言伪造实现
1 | fake_FILE->_mode = 0; |
这里有几点说明
- _IO_wfile_overflow并不是在某些函数中特定调用的而是把_wide_vtable设置成
_IO_wfile_jumps、_IO_wfile_jumps_mmap、_IO_wfile_jumps_maybe_mmap
其一后调用标准库函数会自动去调用 - house of apple2攻击需要标准库的函数对伪造的FILE结构体进行操作,syscall的系统调用无法完成攻击
- FILE->vtable 即 FILE+0xD8
- FILE->_wide_data即FILE+0xA0
- _wide_data->_wide_table即 _wide_data+0xE0
- FILE->mode=0 时,程序会去处理宽字符,即调用
_wide_data
,当作宽字符处理数据,之后就可以去调用_wide_data
相关内容 - 结构体不是必须从heap中伪造,如果能申请到stdin,stdout,stderr等也可进行伪造。例如puts等输出函数会去调用stdout的虚表。或利用largebin attack,unsortedbin attack等去修改
_IO_list_all
使其指向伪造的FILE,之后通过exit的_IO_flush_all_lockp
去调用伪造的FILE的vtable - 跳转到目标地址时
- rdi: FILE->flags 这里如果调用的是
system
,就不能用/bin/sh
了不然无法绕过检查,这里flags的参数可以是" sh"
带两个空格即可绕过检查 - rdx: FILE->_wide_data 这里_wide_data+0xa0可以结合setcontext + 61来控制栈进而执行gadget
- rdi: FILE->flags 这里如果调用的是
如图,调用目标地址时rdi和rdx如下,stdout为FILE
函数调用栈
1 | ► 0 0x7f8eb9e83b9b _IO_wdoallocbuf+43 |
_IO_wfile_underflow_mmap路线
_IO_wfile_jumps_mmap
会调用_IO_wfile_underflow_mmap
- FILE->mode = 0 (_IO_flush_all_lockp 控制流判断条件)
- FILE->_IO_write_ptr > FILE->_IO_write_base (_IO_flush_all_lockp 控制流判断条件)
- FILE->_flag & 4 == 0 (_IO_wfile_underflow_mmap 控制流判断条件)
- FILE->_wide_data->_IO_read_ptr >= FILE->_wide_data->_IO_read_end (_IO_wfile_underflow_mmap 控制流判断条件)
- FILE->_IO_read_ptr < FILE->_IO_read_end (_IO_wfile_underflow_mmap 控制流判断条件)
- FILE->_wide_data->_IO_buf_base == NULL (_IO_wfile_underflow_mmap 控制流判断条件)
- FILE->_wide_data->_IO_save_base == NULL (_IO_wfile_underflow_mmap 控制流判断条件)
- FILE->_wide_data->_IO_buf_base == 0 (_IO_wdoallocbuf 控制流判断条件)
- FILE->_flags & 2 != 0 (_IO_wdoallocbuf 控制流判断条件)
- FILE->_wide_data->_wide_vtable + 0x68 == 要执行的代码地址 (ALLOCATE函数指针偏移0x68)
c语言伪造实现
1 | fake_FILE->_mode = 0; |
_IO_wdefault_xsgetn 路线
_IO_helper_jumps,_IO_wmem_jumps,_IO_wstr_jumps,_IO_wstrn_jumps
均可调用_IO_wdefault_xsgetn
- FILE->mode > 0 (_IO_flush_all_lockp 控制流判断条件,__wunderflow 控制流判断条件)
- FILE->_wide_data->_IO_write_ptr > FILE->_wide_data->_IO_write_base (_IO_flush_all_lockp 控制流判断条件,_IO_switch_to_wget_mode 控制流判断条件)
- rdx != 0 (_IO_wdefault_xsgetn 控制流判断条件)
- FILE->_wide_data->_IO_read_end - FILE->_wide_data->_IO_read_ptr <= 0 (_IO_wdefault_xsgetn 控制流判断条件)
- FILE->_flags & 0x800 != 0 (__wunderflow 控制流判断条件)
- FILE->_wide_data->_wide_vtable + 0x18 == 要执行的代码地址 (OVERFLOW函数指针偏移0x18)
c语言伪造实现
1 | fake_FILE->_mode = 1; |
相关结构体源码
_IO_FILE
去掉了#if不满足的部分
1 | /usr/include/bits/types/struct_FILE.h |
_IO_wide_data
1 | glibc libio/libio.h |
IO_FILE_plus
1 | glibc libio/libioP.h |
IO_jump_t
1 | glibc libio/libioP.h |
实例
分析
libc2.35 bin需key异或,无hook
UAF
沙箱白名单,ORW
add仅能申请0x78,0xe8两种大小,每个大小的堆限制申请4次
add,delete,show,edit四功能齐全
show限制两次
利用UAF泄露heapbase和key,并申请到heap header,修改tcache的数量,free相关chunk进入unsortedbin泄露libc
泄露libc后再次利用UAF申请到stdout,伪造stdout,调用puts实现攻击从而getshell
exp
这里用的_IO_wfile_overflow
的路线
1 | from pwn import * |