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;
};