0%

House Of Pig-原理

House Of Pig

通过 libc-2.31 下的 largebin attack 以及 FILE 结构利用,来配合 libc-2.31 下的 tcache stashing unlink attack 进行组合利用的方法

  • 运用场景:
    • 主要适用于程序中仅有 calloc 函数来申请 chunk,而没有调用 malloc 函数的情况
  • 核心技术点:
    • 利用了 glibc 中 IO_str_overflow 函数内会连续调用 malloc,memcpy,free 函数的特点,并且这三个函数的参数都可以由 FILE 结构内的数据来控制

House Of Pig 原理

House Of Pig 通常是配合 tcache_stashing_unlink 使用的

在 tcache 中有如下检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);

if ((victim = last (bin)) != bin)
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;

if (av != &main_arena)
set_non_main_arena (victim);
check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
  • 但 small chunk 即将进入 tcache 时,就会触发以上检查
  • 可以发现程序只检查了 bck->fd 是否等于自己,但 bck 是可以被我们伪造的

函数 calloc 可以越过 tcache,去直接使用 smallbin,我们可以利用 UAF 修改 small chunk->bk(同时伪造 fake chunk->fd),然后使用 calloc 去申请它,并将 chunk->bk 指向的 fake chunk 给接入 tcache

这就是 tcache_stashing_unlink 的基本思路,但这个思路在全使用 calloc 的程序中并不适用,因为没有 malloc 就意味着 fake chunk 没有办法被申请出来

这时就要借助 IO_str_overflow 了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int
_IO_str_overflow (FILE *fp, int c)
{
int flush_only = c == EOF;
size_t pos;
if (fp->_flags & _IO_NO_WRITES)
return flush_only ? 0 : EOF;
if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
{
......
}
pos = fp->_IO_write_ptr - fp->_IO_write_base;
if (pos >= (size_t) (_IO_blen (fp) + flush_only))
{
if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */
return EOF;
else
{
......
if (new_size < old_blen)
return EOF;
new_buf = malloc (new_size); /* key1 */
if (new_buf == NULL)
{
......
}
if (old_buf)
{
memcpy (new_buf, old_buf, old_blen); /* key2 */
free (old_buf); /* key3 */
/* Make sure _IO_setb won't try to delete _IO_buf_base. */
fp->_IO_buf_base = NULL;
}
......
}
}

if (!flush_only)
*fp->_IO_write_ptr++ = (unsigned char) c;
if (fp->_IO_write_ptr > fp->_IO_read_end)
fp->_IO_read_end = fp->_IO_write_ptr;
return c;
}
libc_hidden_def (_IO_str_overflow)
  • 对于这个函数我们只需要关注3个函数即可:malloc memcpy free
  • 如果在此之前我们已经布置好了 tcache_stashing_unlink 并把 free hook 设置为 fake chunk,那么 malloc 就会申请到 free hookmemcpy 就会往 free hook 中写入数据,free 就会触发 free hook

劫持 _IO_list_all 将其 vtable 由 _IO_file_jumps 修改为 _IO_str_jumps,那么当原本应该调用 IO_file_overflow 的时候,就会转而调用 IO_str_overflow

  • 控制 _IO_buf_end_IO_buf_base,我们就可以控制 malloc 的 size 以及写入的数据

House Of Pig 利用姿势

House Of Pig 文字版详细过程:

  • 先用 UAF 漏洞泄露 libc 地址 和 heap 地址
  • 再用 UAF 修改 largebin 内 chunk 的 fd_nextsize 和 bk_nextsize 位置,完成一次 largebin attack,将一个堆地址写到 __free_hook-0x8 的位置,使得满足之后的 tcache stashing unlink attack 需要目标 fake chunk 的 bk 位置内地址可写的条件
  • 先构造同一大小的5个 tcache,继续用 UAF 修改该大小的 smallbin 内 chunk 的 fd 和 bk 位置,完成一次 tcache stashing unlink attack,由于前一步已经将一个可写的堆地址,写到了__free_hook-0x8 ,所以可以将 __free_hook-0x10 的位置当作一个 fake chunk,放入到 tcache 链表的头部,但是由于没有 malloc 函数,我们无法将他申请出来
  • 最后再用 UAF 修改 largebin 内 chunk 的 fd_nextsize 和 bk_nextsize 位置,完成第二次 largebin attack,将一个堆地址写到 _IO_list_all 的位置,从而在程序退出前 flush 所有 IO 流的时候,将该堆地址当作一个 FILE 结构体,我们就能在该堆地址的位置来构造任意 FILE结构了
  • 在该堆地址构造 FILE 结构的时候,重点是将其 vtable 由 _IO_file_jumps 修改为 _IO_str_jumps,那么当原本应该调用 IO_file_overflow 的时候,就会转而调用如下的 IO_str_overflow
  • 而该函数是以传入的 FILE 地址本身为参数的,同时其中会连续调用 malloc,memcpy,free 函数,且三个函数的参数又都可以被该 FILE 结构中的数据控制,那么适当的构造 FILE 结构中的数据,就可以实现利用 IO_str_overflow 函数中的 malloc 申请出那个已经被放入到 tcache 链表的头部的包含 __free_hook 的 fake chunk
  • 紧接着可以将提前在堆上布置好的数据,通过 IO_str_overflow 函数中的 memcpy 写入到刚刚申请出来的包含 __free_hook 的这个 chunk,从而能任意控制 __free_hook ,这里可以将其修改为 system 函数地址
  • 最后调用 IO_str_overflow 函数中的 free 时,就能够触发 __free_hook ,同时还能在提前布置堆上数据的时候,使其以字符串 “/bin/sh\x00” 开头,那么最终就会执行 system(“/bin/sh”)

版本对 House Of Pig 的影响

适用于 libc-2.31 及以后的新版本 libc