_IOfile attack(2.24)
glibc 2.24
在 glibc 2.23
下,可以通过伪造 __IO_file_plus
来劫持函数调用链,而这个
漏洞在 glibc 2.24
之后被修复了。
现在的 glibc
在调用虚函数之前,会对 vtable
地址的合法性。
static inline const struct _IO_jump_t *
IO_validate_vtable (const struct _IO_jump_t *vtable)
{
uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
const char *ptr = (const char *) vtable;
uintptr_t offset = ptr - __start___libc_IO_vtables;
if (__glibc_unlikely (offset >= section_length))
_IO_vtable_check ();//引发报错的函数
return vtable;
}
利用方式
IO_str_jumps
和 IO_wstr_jumps
这两个结构体 可以绕过 vtable check
。
其中,利用 IO_str_jumps
绕过更简单,IO_wstr_jumps
利用方式也差不多。
const struct _IO_jump_t _IO_str_jumps libio_vtable =
{
JUMP_INIT_DUMMY,//调试发现占0x10
JUMP_INIT(finish, _IO_str_finish),
JUMP_INIT(overflow, _IO_str_overflow),
JUMP_INIT(underflow, _IO_str_underflow),
JUMP_INIT(uflow, _IO_default_uflow),
[...]
};
_IO_str_finsh
的源代码:
void _IO_str_finish (FILE *fp, int dummy)
{
if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
(((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base); //call qword ptr [fp+0E8h]
fp->_IO_buf_base = NULL;
_IO_default_finish (fp, 0);
}
只要绕过
- fp -> _flags == 0
- fp -> _IO_buf_base > 0
的 check, 程序就能调用 (((_IO_strfile *) fp)->_s._free_buffer)
,也就是 fp+0xe8
的函数
exp 构造
- fp->_flags = 0
- vtable = _IO_str_jumps - 0x8 (因为系统会调用
vtable + 0x20
处的函数,_IO_str_jumps - 0x8 + 0x20 = _IO_str_overfflow
) - fp->_IO_buf_base = /bin/sh_addr
- fp+0xe8 = system_addr