买不起的大house之house of kiwi

前言

glibc2.34中取消了 malloc_hook/free_hook 等关键 hook 。这使得原本利用修改这些 hook 来达成控制程序流的方法不再适用。所以在进入glibc高版本后出现了很多利用IO流来控制程序流程的方法,house of kiwi就是其中之一

适用范围

glibc2.23-glibc2.35(漏洞所需要的malloc_assert函数在2.36中被修改,而在2.37中则完全被删除)

使用条件

  1. 能够达成任意已知地址写
  2. 能够触发 __malloc_assert(要把top_chunk的size位的prev_inuse置为0,再申请一个比top_chunk大的堆块就可以触发了这个断言,因此我们往往需要修改top_chunk的size位)
    如果需要打ORW的话还需要_IO_helper_jumps可写(只有一些特殊的版本下是可写的,大多时候还是不可写的)

利用原理

在glibc中有下列这样一段函数

#if IS_IN (libc)
#ifndef NDEBUG
# define __assert_fail(assertion, file, line, function)			\
	 __malloc_assert(assertion, file, line, function)

extern const char *__progname;

static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
		 const char *function)
{
  (void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
		     __progname, __progname[0] ? ": " : "",
		     file, line,
		     function ? function : "", function ? ": " : "",
		     assertion);
  fflush (stderr);
  abort ();
}
#endif
#endif

从源码中我们可以看到函数调用了fflush(stderr),而这个函数调用后会调用_IO_file_jumps中的sync指针
下面我们来看看fflush函数具体是如何实现的

#define fflush(s) _IO_fflush (s)
int
_IO_fflush (FILE *fp)
{
  if (fp == NULL)
    return _IO_flush_all ();
  else
    {
      int result;
      CHECK_FILE (fp, EOF);
      _IO_acquire_lock (fp);
      result = _IO_SYNC (fp) ? EOF : 0;
      _IO_release_lock (fp);
      return result;
    }
}
libc_hidden_def (_IO_fflush)

我们使用gdb调试进入fflush函数中

# 此时的rbp指向_IO_file_jumps这个虚表的地址
0x7f1ac0617d45 <fflush+117>    mov    rcx, rbp
0x7f1ac0617d48 <fflush+120>    sub    rcx, rdx
0x7f1ac0617d4b <fflush+123>    cmp    rax, rcx
0x7f1ac0617d4e <fflush+126>    jbe    fflush+216                
0x7f1ac0617d50 <fflush+128>    mov    rdi, rbx
► 0x7f1ac0617d53 <fflush+131>    call   qword ptr [rbp + 0x60]        <setcontext+61>
		rdi: 0x7f1ac077d5e0 (_IO_2_1_stderr_) ◂— 0xfbad2887
		rsi: 0x7ffedb61cd20 ◂— 0x616d203a6e69616d ('main: ma')
		rdx: 0x7f1ac077d8c0 (_IO_helper_jumps) ◂— 0x0
		rcx: 0xc00
0x7f1ac0617d56 <fflush+134>    xor    r8d, r8d
0x7f1ac0617d59 <fflush+137>    test   eax, eax
0x7f1ac0617d5b <fflush+139>    setne  r8b
0x7f1ac0617d5f <fflush+143>    neg    r8d
0x7f1ac0617d62 <fflush+146>    test   dword ptr [rbx], 0x8000

进入fflush函数之后我们发现它的其中有一个call qword ptr [rbp + 0x60]的汇编代码,在执行这行语句时rbp指向_IO_file_jumps这个虚表的地址。而rbp+0x60则是一个叫做__sync的指针
![[Z{4E]YN7JM~T{QWOHB2IYOQ.png]]

那么我们很自然的就会想到,如果我们能够控制_IO_file_jumps这个虚表,然后控制/修改__sync这个指针比如修改为one_gadget,随后想办法调用fflush函数就可以拿到shell了

kiwi怎么打ORW

条件:kiwi打ORW需要_IO_file_jumps可写
利用方法和原理:

如果我们要打ORW的话我们就需要利用到setcontext函数利用setcontext来进行ORW-优快云博客
我们可以将__sync这个指针修改为setcontext+61,而高版本setcontext中的代码片段赋值用的是rdx寄存器+偏移,而在执行上述的call qword ptr [rbp + 0x60]的时候rdx寄存器正好指向_IO_helper_jumps,所以rdx+偏移都是_IO_helper_jumps中的内容,这也就是为什么说需要_IO_helper_jumps可写。
但是_IO_helper_jumps在高版本中大多时候不可写,只有一些小版本可写。
![[Pasted image 20250305150747.png]]

那么如果我们可以控制_IO_helper_jumps那么我们要做的就是

  1. 布置好ROP
  2. 控制_IO_file_jumps + 0x60setcontext + 61
  3. 控制_IO_helper_jumps+0xa0_IO_helper_jumps+0xa8分别为ROP地址ret地址

kiwi利用方法总结

1.拿shell
  1. 能够达成任意已知地址写,来想办法控制_IO_file_jumps中的__sync指针,修改__sync指针为one_gadget
  2. 控制top_chunksize位的大小并将prev_inuse置为0
  3. 申请一个比我们修改后的top_chunk的size大的chunk块,就可以触发__malloc_assert
2.打ORW
  1. 能够达成任意已知地址写,来想办法控制_IO_file_jumps中的__sync指针为setcontext+61
  2. 在可控空间中布置ROP(一般是堆块中布置)
  3. 控制_IO_helper_jumps+0xa0_IO_helper_jumps+0xa8分别为ROP地址ret地址
  4. 控制top_chunksize位的大小并将prev_inuse置为0
  5. 申请一个比我们修改后的top_chunk的size大的chunk块,就可以触发__malloc_assert

注意:
在glibc-2.36中__malloc_assert就被修改掉了

_Noreturn static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
         const char *function)
{
  __libc_message (do_abort, "\
Fatal glibc error: malloc assertion failure in %s: %s\n",
          function, assertion);
  __builtin_unreachable ();
}

而glibc-2.37则更是逆天
直接删除了__malloc_assert

参考:
1.House OF Kiwi-安全KER - 安全资讯平台
2.文章 - house of kiwi(shell及orw例题分析) - 先知社区
3.house系2 - 望权栈 - 博客园
4.文章 - IO利用之House of kiwi & House of emma - 先知社区
5.Glibc堆利用之house of系列总结 - roderick - record and learn!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值