house of apple1
[TOC] 参考博客 house of apple1 需要和其他方法结合而进行后续的FSOP利用 _IO_wstrn_jumps
前言
利用条件 使用house of apple的条件为: 1、程序从main函数返回或能调用exit函数 2、能泄露出heap地址和libc地址 3、 能使用一次largebin attack(一次即可)
- 当程序从main函数返回或者执行exit函数的时候,均会调用fcloseall函数,该调用链为exit->fcloseall->_IO_cleanup->_IO_flush_all_lockp->_IO_OVERFLOW 最后会遍历_IO_list_all存放的每一个IO_FILE结构体,如果满足条件的话,会调用每个结构体中vtable->_overflow函数指针指向的函数。
- 使用largebin attack可以劫持_IO_list_all变量,将其替换为伪造的IO_FILE结构体,而在此时,我们其实仍可以继续利用某些IO流函数去修改其他地方的值。要想修改其他地方的值,就离不开_IO_FILE的一个成员_wide_data的利用
核心
此时在堆上伪造一个_IO_FILE结构体并已知其地址为A,将A + 0xd8(vtable对应的偏移)替换为_IO_wstrn_jumps地址,A + 0xa0(_wide_data对应的偏移)设置为B,并设置其他成员以便能调用到_IO_OVERFLOW。exit函数则会一路调用到_IO_wstrn_overflow函数,并将B至B + 0x30的地址区域的内容都替换为A + 0xf0或者A + 0x1f0。
- struct _IO_wide_data *_wide_data在_IO_FILE中的偏移为0xa0
- 伪造_IO_FILE结构体的时候,伪造_wide_data变量,然后通过某些函数,比如_IO_wstrn_overflow就可以将已知地址空间上的某些值修改为一个已知值。
static wint_t
_IO_wstrn_overflow (FILE *fp, wint_t c)
{
_IO_wstrnfile *snf = (_IO_wstrnfile *) fp;
if (fp->_wide_data->_IO_buf_base != snf->overflow_buf)
{
_IO_wsetb (fp, snf->overflow_buf,
snf->overflow_buf + (sizeof (snf->overflow_buf)
/ sizeof (wchar_t)), 0);
//只要控制了fp->_wide_data,就可以控制从fp->_wide_data开始一定范围内的内存的值,也就等同于任意地址写已知地址。
fp->_wide_data->_IO_write_base = snf->overflow_buf;
fp->_wide_data->_IO_read_base = snf->overflow_buf;
fp->_wide_data->_IO_read_ptr = snf->overflow_buf;
fp->_wide_data->_IO_read_end = (snf->overflow_buf
+ (sizeof (snf->overflow_buf)
/ sizeof (wchar_t)));
}
fp->_wide_data->_IO_write_ptr = snf->overflow_buf;
fp->_wide_data->_IO_write_end = snf->overflow_buf;
return c;
}
- 有时候需要绕过_IO_wsetb函数里面的free
#define _IO_FLAGS2_USER_WBUF 8
//设置f->_flags2为8即可绕过
void
_IO_wsetb (FILE *f, wchar_t *b, wchar_t *eb, int a)
{
if (f->_wide_data->_IO_buf_base && !(f->_flags2 & _IO_FLAGS2_USER_WBUF))
free (f->_wide_data->_IO_buf_base); // 其不为0的时候不要执行到这里
f->_wide_data->_IO_buf_base = b;
f->_wide_data->_IO_buf_end = eb;
if (a)
f->_flags2 &= ~_IO_FLAGS2_USER_WBUF;
else
f->_flags2 |= _IO_FLAGS2_USER_WBUF;
}
利用思路
思路一:修改tcache线程变量
该思路需要借助house of pig的思想,利用_IO_str_overflow中的malloc进行任意地址分配,memcpy进行任意地址覆盖。 利用步骤如下:
- 伪造至少两个_IO_FILE结构体
- 第一个_IO_FILE结构体执行_IO_OVERFLOW的时候,利用_IO_wstrn_overflow函数修改tcache全局变量为已知值,也就控制了tcache bin的分配
- 第二个_IO_FILE结构体执行_IO_OVERFLOW的时候,利用_IO_str_overflow中的malloc函数任意地址分配,并使用memcpy使得能够任意地址写任意值
- 利用两次任意地址写任意值修改pointer_guard和IO_accept_foreign_vtables的值绕过_IO_vtable_check函数的检测(或者利用一次任意地址写任意值修改libc.got里面的函数地址,很多IO流函数调用strlen/strcpy/memcpy/memset等都会调到libc.got里面的函数)
- 利用一个_IO_FILE,随意伪造vtable劫持程序控制流即可
因为可以已经任意地址写任意值了,所以这可以控制的变量和结构体非常多,也非常地灵活,需要结合具体的题目进行利用,比如题目中_IO_xxx_jumps映射的地址空间可写的话直接修改其函数指针即可。
思路二:修改mp_结构体
该思路与上述思路差不多,不过对tcachebin分配的劫持是通过修改mp_.tcache_bins这个变量。打这个结构体的好处是在攻击远程时不需要爆破地址,因为线程全局变量、tls结构体的地址本地和远程并不一定是一样的,有时需要爆破。 利用步骤如下:
- 伪造至少两个_IO_FILE结构体
- 第一个_IO_FILE结构体执行_IO_OVERFLOW的时候,利用_IO_wstrn_overflow函数修改mp_.tcache_bins为很大的值,使得很大的chunk也通过tcachebin去管理
- 接下来的过程与上面的思路是一样的
思路三:修改pointer_guard线程变量之house of emma
该思路其实就是house of apple + house of emma。 利用步骤如下:
- 伪造两个_IO_FILE结构体
- 第一个_IO_FILE结构体执行_IO_OVERFLOW的时候,利用 _IO_wstrn_overflow函数修改tls结构体pointer_guard的值为已知值
- 第二个_IO_FILE结构体用来做house of emma利用即可控制程序执行流
思路四:修改global_max_fast全局变量
这个思路也很灵活,修改掉这个变量后,直接释放超大的chunk,去覆盖掉point_guard或者tcache变量。我称之为house of apple + house of corrision。
利用过程与前面也基本是大同小异,就不在此详述了。
其实也有其他的思路,比如还可以劫持main_arena,不过这个结构体利用起来会更复杂,所需要的空间将更大。而在上述思路的利用过程中,可以选择错位构造_IO_FILE结构体,只需要保证关键字段满足要求即可,这样可以更加节省空间。
相关源码
_IO_wstrn_jumps
const struct _IO_jump_t _IO_wstrn_jumps libio_vtable attribute_hidden =
{
JUMP_INIT_DUMMY,
JUMP_INIT(finish, _IO_wstr_finish),
JUMP_INIT(overflow, (_IO_overflow_t) _IO_wstrn_overflow),
JUMP_INIT(underflow, (_IO_underflow_t) _IO_wstr_underflow),
JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
JUMP_INIT(xsputn, _IO_wdefault_xsputn),
JUMP_INIT(xsgetn, _IO_wdefault_xsgetn),
JUMP_INIT(seekoff, _IO_wstr_seekoff),
JUMP_INIT(seekpos, _IO_default_seekpos),
JUMP_INIT(setbuf, _IO_default_setbuf),
JUMP_INIT(sync, _IO_default_sync),
JUMP_INIT(doallocate, _IO_wdefault_doallocate),
JUMP_INIT(read, _IO_default_read),
JUMP_INIT(write, _IO_default_write),
JUMP_INIT(seek, _IO_default_seek),
JUMP_INIT(close, _IO_default_close),
JUMP_INIT(stat, _IO_default_stat),
JUMP_INIT(showmanyc, _IO_default_showmanyc),
JUMP_INIT(imbue, _IO_default_imbue)
};
_IO_wstrnfile涉及到的结构体
//0x10
struct _IO_str_fields
{
_IO_alloc_type _allocate_buffer_unused;
_IO_free_type _free_buffer_unused;
};
//0xd8+0x8=0xe0
struct _IO_streambuf
{
FILE _f;
const struct _IO_jump_t *vtable;
};
typedef struct _IO_strfile_
{
struct _IO_streambuf _sbf; //0xe0
struct _IO_str_fields _s; //0x10
} _IO_strfile;
typedef struct
{
_IO_strfile f;
char overflow_buf[64];
} _IO_strnfile;
typedef struct
{
//0xf0
_IO_strfile f;
wchar_t overflow_buf[64]; // overflow_buf在这里********
} _IO_wstrnfile
_IO_wide_data结构体
struct _IO_wide_data
{
0x0 wchar_t *_IO_read_ptr; /* Current read pointer */
0x8 wchar_t *_IO_read_end; /* End of get area. */
0x10 wchar_t *_IO_read_base; /* Start of putback+get area. */
0x18 wchar_t *_IO_write_base; /* Start of put area. */
0x20 wchar_t *_IO_write_ptr; /* Current put pointer. */
0x28 wchar_t *_IO_write_end; /* End of put area. */
0x30 wchar_t *_IO_buf_base; /* Start of reserve area. */
0x38 wchar_t *_IO_buf_end; /* End of reserve area. */
0x40 wchar_t *_IO_save_base;
0x48 wchar_t *_IO_backup_base;
0x50 wchar_t *_IO_save_end;
__mbstate_t _IO_state;
__mbstate_t _IO_last_state;
struct _IO_codecvt _codecvt;
wchar_t _shortbuf[1];
0xe0 const struct _IO_jump_t *_wide_vtable;
};