在libc2.35及以后,glibc将许多的hook都给移除了,例如malloc_hook,free_hook等,导致一些常见的许多堆攻击利用方式几乎都失效了。 高版本后,_IO_FILE 的伪造和对 IO 流的劫持基本上是堆工具的主流思路,但是菜鸡一直看不懂,最近沉下心来看了roderick 佬的文章貌似看懂了点于是小记一下。
利用条件
能控制程序执行 IO
操作,包括但不限于:从 main
函数返回、调用 exit
函数、通过__malloc_assert
触发
已知 heap
地址和 glibc
地址
能控制_IO_FILE
的 vtable
和_wide_data
,一般使用 largebin attack
去控制
利用原理
stdin/stdout/stderr
这三个_IO_FILE
结构体使用的是_IO_file_jumps
这个 vtable
。
1 | pwndbg> p *(struct _IO_FILE_plus *) stdin |
2.24以后的glibc中,这个 vtable
会有一个IO_validate_vtable函数对其指向的地址进行检测,检测代码如下:
1 | /* Perform vtable pointer validation. If validation fails, terminate |
stdin/stdout/stderr
这三个_IO_FILE
结构体中有个 ``_wide_data`成员指向一个和FILE结构体十分相像的wide_data结构体。
1 | pwndbg> p *(struct _IO_wide_data*) 0x7f3cb081cb80 |
两个结构体,都有一个虚表,但是_IO_wfile_jumps 没有进行检查保护,这意味着我们可以把它劫持到我们伪造的虚表,从而控制执行流。
利用_IO_wfile_overflow 函数控制程序执行流
对 fp
的设置如下:
_flags
设置为~(2 | 0x8 | 0x800)
,如果不需要控制rdi
,设置为0
即可;如果需要获得shell
,可设置为sh;
,注意前面有两个空格vtable
设置为_IO_wfile_jumps/_IO_wfile_jumps_mmap/_IO_wfile_jumps_maybe_mmap
地址(加减偏移),使其能成功调用_IO_wfile_overflow
即可_wide_data
设置为可控堆地址A
,即满足*(fp + 0xa0) = A
_wide_data->_IO_write_base
设置为0
,即满足*(A + 0x18) = 0
_wide_data->_IO_buf_base
设置为0
,即满足*(A + 0x30) = 0
_wide_data->_wide_vtable
设置为可控堆地址B
,即满足*(A + 0xe0) = B
_wide_data->_wide_vtable->doallocate
设置为地址C
用于劫持RIP
,即满足*(B + 0x68) = C
函数的调用链如下:
1
2
3
4_IO_wfile_overflow
_IO_wdoallocbuf
_IO_WDOALLOCATE
*(fp->_wide_data->_wide_vtable + 0x68)(fp)
例子思路
- 利用largebin attack向IO_list_all里面写入一个可控的堆地址
- 在这个堆块里面同时伪造一个_IO_list_all结构体和IO_wide_data结构体,以及他们对应的vtable指针
- _IO_list_all结构体的vtable指针指向 _IO_wfile_jumps来绕过检查,而 _wide_data的结构体指向我们伪造的虚表即可
模版:
1 |
|
exp
1 | #!/usr/bin/env python |