house of apple2

文章目录

  • 原文链接
  • 利用条件
  • 利用原理
  • 劫持思路
    • _IO_wfile_overflow
    • _IO_wfile_underflow_mmap

原文链接

利用条件

  • 泄漏 libc_baseheap_base
  • 触发 IO 流操作,常见 exit__malloc_assert 触发
  • 控制 _IO_FILEvtable_wide_data,一般通过 largebin attack 进行控制

利用原理

核心:glibc_IO_wide_data 中的 _wide_vtable 不存在检查。

来看下对 _IO_file_jumps 中虚表指针调用逻辑:

#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)
#define JUMP1(FUNC, THIS, X1) (_IO_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
#if _IO_JUMPS_OFFSET
# define _IO_JUMPS_FUNC(THIS) \
  (IO_validate_vtable                                                   \
   (*(struct _IO_jump_t **) ((void *) &_IO_JUMPS_FILE_plus (THIS)	\
			     + (THIS)->_vtable_offset)))
#else
# define _IO_JUMPS_FUNC(THIS) (IO_validate_vtable (_IO_JUMPS_FILE_plus (THIS)))
#endif

可以看到这里是调用了 IO_validate_vtable 对虚表进行了检查的:

/* Perform vtable pointer validation.  If validation fails, terminate
   the process.  */
static inline const struct _IO_jump_t *
IO_validate_vtable (const struct _IO_jump_t *vtable)
{
  /* Fast path: The vtable pointer is within the __libc_IO_vtables
     section.  */
  uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
  uintptr_t ptr = (uintptr_t) vtable;
  uintptr_t offset = ptr - (uintptr_t) __start___libc_IO_vtables;
  if (__glibc_unlikely (offset >= section_length))
    /* The vtable pointer is not in the expected section.  Use the
       slow path, which will terminate the process if necessary.  */
    _IO_vtable_check ();
  return vtable;
}

再来看下对 _IO_wide_data 中虚表 _IO_wfile_jumps 的调用逻辑:

#define _IO_WOVERFLOW(FP, CH) WJUMP1 (__overflow, FP, CH)
#define WJUMP1(FUNC, THIS, X1) (_IO_WIDE_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
#define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS)
#define _IO_WIDE_JUMPS(THIS) \
  _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE, _wide_data)->_wide_vtable

可以看到这里并没有对虚表进行检查,而是直接调用的虚表指针。

劫持思路

  • 劫持 IO_FILEvtable_IO_wfile_jumps
  • 控制 _wide_data 为可控地址空间
  • 控制 _wide_data->_wide_vtable 为可控地址空间
  • 触发 IO 流,最终调用 _IO_Wxxxx 函数即可劫持程序执行流

所以这里我们需要找到执行 _IO_Wxxxx 函数的链子,原文中给出了三条链子:

  • _IO_wfile_overflow
  • _IO_wfile_underflow_mmap
  • _IO_wdefault_xsgetn

_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

接下来跟进源码去看看为啥这么设置。
_IO_wfile_overflow

wint_t
_IO_wfile_overflow (FILE *f, wint_t wch)
{
  if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
  {
      f->_flags |= _IO_ERR_SEEN;
      __set_errno (EBADF);
      return WEOF;
  }
  /* If currently reading or no buffer allocated. */
  if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0)
  {
      /* Allocate a buffer if needed. */
      if (f->_wide_data->_IO_write_base == 0)
	  {
	  	_IO_wdoallocbuf (f);
......

这里当满足:

  • _flags & _IO_NO_WRITES = 0
  • _flags & _IO_CURRENTLY_PUTTING = 0
  • _wide_data->_IO_write_base = 0
    就会调用到 _IO_wdoallocbuf(f) 函数
#define _IO_NO_WRITES         0x0008 
#define _IO_CURRENTLY_PUTTING 0x0800

_IO_wdoallocbuf

void _IO_wdoallocbuf (FILE *fp)
{
  if (fp->_wide_data->_IO_buf_base)
    return;
  if (!(fp->_flags & _IO_UNBUFFERED))
    if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF)	// _IO_WXXXX调用
      return;
  _IO_wsetb (fp, fp->_wide_data->_shortbuf, fp->_wide_data->_shortbuf + 1, 0);
}

这里当满足:

  • _wide_data->_IO_buf_base = 0
  • _flags & _IO_UNBUFFERED = 0
#define _IO_UNBUFFERED        0x0002

就会调用到 _IO_WDOALLOCATE 函数,所以通过伪造 _wide_data->_wide_vtable->doallocate 即可劫持程序执行流。

_IO_wfile_underflow_mmap

fp 的设置如下:

  • _flags 设置为 ~4,如果不需要控制 rdi,设置为 0 即可;如果需要获得 shell,可设置为 sh;,注意前面有个空格
  • vtable 设置为_IO_wfile_jumps_mmap 地址(加减偏移),使其能成功调用 _IO_wfile_underflow_mmap 即可
  • _IO_read_ptr < _IO_read_end,即满足 *(fp + 8) < *(fp + 0x10)
  • _wide_data 设置为可控堆地址 A,即满足 *(fp + 0xa0) = A
  • _wide_data->_IO_read_ptr >= _wide_data->_IO_read_end,即满足 *A >= *(A + 8)
  • _wide_data->_IO_buf_base 设置为 0,即满足 *(A + 0x30) = 0
  • _wide_data->_IO_save_base 设置为 0 或者合法的可被 free 的地址,即满足 *(A + 0x40) = 0
  • _wide_data->_wide_vtable 设置为可控堆地址 B,即满足 *(A + 0xe0) = B
  • _wide_data->_wide_vtable->doallocate 设置为地址 C 用于劫持 RIP,即满足 *(B + 0x68) = C

其他的就不多说了就是上面的分析方法,没啥特别的,最后一个链子大家看原文章吧。

你可能感兴趣的:(PWN—house系列,house,of,apple2)