house of obstack

house of obstack

漏洞成因

堆溢出

适用范围

  • 2.23—— 至今
  • 可以执行一次 largebin attack
  • 可以触发 IO 流操作

简述

house of obstack 是 large bin attack(或能达到同样效果的手法也可以)+ FSOP 组合出来的攻击,通过 large bin attack 向 _IO_list_all 中写入一个堆地址以此来伪造一个 IO_FILE 。在 glibc2.23 之后的版本针对 IO_FILE 结构体的 vtable 地址做了检查,该手法通过小幅度改变 vtable 地址,让执行流偏离了正常的调用链,通过伪造 IO_FILE 结构体中的某些字段,最终实现任意地址执行且参数可控的效果.

利用条件为:

  • ​ 可以泄露 libc 地址和堆地址
  • ​ 可以使用任意地址写一个堆地址(通常是使用 large bin attack )
  • ​ 可以从 main 函数返回或者调用 exit 函数

利用原理

说明: 以下的 glibc 源代码均来自 glibc-2.36

先来看下 exit 函数的宏观调用链

exit => __run_exit_handlers => _IO_cleanup => _IO_flush_all_lockp

第一次去改变 exit 正常的执行流是在这里的 _IO_OVERFLOW 这个宏 ,它位于 _IO_flush_all_lockp 函数中(如下)

int
_IO_flush_all_lockp (int do_lock)
{
   
  int result = 0;
  FILE *fp;
    
......
    
      if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
	   || (_IO_vtable_offset (fp) == 0
	       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
				    > fp->_wide_data->_IO_write_base))
	   )
	  && _IO_OVERFLOW (fp, EOF) == EOF)
	result = EOF;
...... 
  return result;
}

伪造 IO_FILE 结构体时, vtable 也是可控的(对这个地址做了范围的合法性检查),将原本 vtable 中的 _IO_file_jumps 改成 _IO_obstack_jumps 可以绕过检查(二者的距离很近)

_IO_OVERFLOW 寻找的函数指针是位于起始地址(正常情况下的 _IO_file_jumps )偏移 0x18 的位置,因此我们只要将正常的 _IO_file_jumps 改成 _IO_obstack_jumps+32 ,实际上 _IO_OVERFLOW 执行的就是 _IO_obstack_jumps+32+0x18 的函数指针,该位置的函数指针为 _IO_obstack_xsputn (如下)

pwndbg> p &_IO_obstack_jumps 
$16 = (const struct _IO_jump_t *) 0x7ffff7e163c0 <_IO_obstack_jumps>
pwndbg> telescope 0x7ffff7e163c0+0x18+32
00:0000│  0x7ffff7e163f8 (_IO_obstack_jumps+56) —▸ 0x7ffff7c88590 (_IO_obstack_xsputn) ◂— endbr64 

根据上面所说的伪造完成后(先不考虑要绕过检查的字段),此时程序会执行到 _IO_obstack_xsputn 函数上,从该函数开始会存在一条调用链如下

_IO_obstack_xsputn => _obstack_newchunk => CALL_CHUNKFUN => (*(h)->chunkfun)((h)->extra_arg, (size))

一条新的利用链,伪造 vtable_IO_obstack_jumps,然后调用到_IO_obstack_xsputn,紧接着调用 obstack_grow,其代码为:

#define obstack_grow(OBSTACK, where, length)                      \
  __extension__                                   \
    ({
      struct obstack *__o = (OBSTACK);                       \
       int __len = (length);                              \
       if (_o->next_free + __len > __o->chunk_limit)                  \
     _obstack_newchunk (__o, __len);                      \
       memcpy (__o->next_free, where, __len);                     \
       __o->next_free += __len;                           \
       (void) 0; })

然后在_obstack_newchunk 调用了 CALL_CHUNKFUN 这个宏

void
_obstack_newchunk (struct obstack *h, int length)
{
   
  struct _obstack_chunk *old_chunk = h->chunk;
  struct _obstack_chunk *new_chunk;
  long new_size;
  long obj_size = h->next_free - h->object_base;
  long i;
  long already;
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值