Glibc高版本堆利用方法总结

截止到目前,主要总结在2.35~2.37之间仍然残存的堆利用手法。注意,本文的撰写时间为2023-03-01



可以在Bilibili上观看视频进行学习:

或者在Youtube上观看视频进行学习:


进入到glibc-2.31之后,很多原有的堆利用方法就失效,因此glibc给堆分配机制陆陆续续打上了很多patch,目前来看,与堆利用有关的patch有:

  • tcachebin堆指针异或加密(glibc-2.32引入)
  • tcahebin链的数量检查(glibc-2.33引入)
  • fastbin堆指针异或加密(glibc-2.32引入)
  • 堆内存对齐检查(glibc-2.32引入)
  • 移除__malloc_hook__free_hookglibc-2.34引入)
  • 引入tcache_key作为tcachekey检查(glibc-2.34引入)
  • __malloc_assert 移除掉IO处理函数(glibc-2.36引入)
  • 移除__malloc_assert函数(glibc-2.37引入)
  • global_max_fast的数据类型修改为uint8_tglibc-2.37引入)

根据目前已有的patch,结合之前已有的堆利用方法,总结2.35版本之后的攻击向量与攻击面,给出针对这些攻击面的攻击手段,并对某些攻击面的利用方法进行思考和拓展。如有错误或遗漏,欢迎批评指正。

本文所提到的house of系列的利用手段,可以参考我之前写的博客Glibc堆利用之house of系列总结 - roderick - record and learn! (roderickchan.cn)

1-攻击向量

1-1 tcachebin

事实上,在泄露地址的基础上劫持tcachebinnext,依然可以任意地址分配。

1-1-1 绕过指针保护

绕过指针异或的保护方法主要有两种:

  • tcachebin链表中只有一个chunk的时候,此时chunk->next << 12即可得到堆地址。

  • tcachebin链表的前两个chunk的地址相差不是很大的时候,可以用下面的公式计算:

    1
    2
    3
    4
    5
    6
    7
    8
    
    def calc_heap(addr):
        s = hex(addr)[2:]
        s = [int(x, base=16) for x in s]
        res = s.copy()
        for i in range(9):
            res[3+i] ^= res[i]
        res = "".join([hex(x)[2:] for x in res])
        return int16_ex(res)

    这里的addr就是头部chunk的加密后的next,只泄露一次就能还原出来。

1-1-2 劫持tcache_ptheread_struct

这个结构体的重要性不言而喻,劫持了这个结构体可以控制tcachebin的分配。一般可以用tcachebin stash unlink或者largebin attack劫持。

1-1-3 修改线程tcache变量

tls区域,有一个线程变量tcache,如果能用largebin attack修改tcache变量,也可以控制tcache的分配。

1-1-4 修改mp_结构体

关注与tcache有关的几个变量:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
struct malloc_par
{
	//......
#if USE_TCACHE
  /* Maximum number of buckets to use.  */
  size_t tcache_bins;
  size_t tcache_max_bytes;
  /* Maximum number of chunks in each bucket.  */
  size_t tcache_count;
  /* Maximum number of chunks to remove from the unsorted list, which
     aren't used to prefill the cache.  */
  size_t tcache_unsorted_limit;
#endif
};

修改掉tcache_bins可以把很大的chunktcachebin管理;修改掉tcache_count可以控制链表的chunk的数量。tcache_max_bytes目前没啥用,tcache_unsorted_limit可以影响unsortedbin链表的遍历过程。

1-2 fastbin

1-2-1 house of corrosion

使用的范围只能在2.35~2.37,进入到2.37之后,global_max_fast的类型被修改为int8_t,使用该技巧可以控制的地址范围大大缩小。

有关house of corrosion的技巧可以参考House-of-Corrosion 一种新的堆利用技巧 - 先知社区 (aliyun.com)

1-2-2 tcache reverse into fastbin

目前检查了对齐,所以要注意控制的地址要是0x?0结尾,否则报错。利用效果是任意地址写一个libc地址。

虽然0x?0写的是加密后的堆地址,但是0x?8会写上tcache_key,这也是可以利用的点。而且,在写上地址后,还能分配到该处。其利用过程如下:

  • 分配13fastbin范围内的chunk,假设大小为A
  • 全部释放这13chunk
  • 分配7个,把tcachebin[A]耗尽
  • fastbin最后一个chunkfd修改为addr
  • 调用一次malloc(A)即可触发tcache reverse into fastbin,可以分配到addr,也能给addr/addr+8处写上地址/数

1-3 smallbin

1-3-1 house of lore

很显然,house of lore依然可以使用,但是house of lore使用的时候,一方面是需要满足victim->fd->bk == victim;另一方面,需要绕过下面讲的tcache stash unlink流程。除此之外,还需要注意内存对齐的问题。

在我之前的博客中,分析house of rust的时候总结过这个利用手法。

第一个技巧叫 tcachebin stash unlinking,下面称之为 TSU 技巧:

  • tcachebin[A] 为空
  • smallbin[A]8
  • 修改第 8smallbin chunkbkaddr
  • 分配 malloc(A) 的时候,addr+0x10 会被写一个 libc 地址

第二个技巧叫 tcachebin stash unlinking+,下面称之为 TSU+ 技巧:

  • tcachebin[A] 为空
  • smallbin[A]8
  • 修改第 7smallbin chunkbkaddr,还要保证 addr+0x18 是一个合法可写的地址
  • 分配 malloc(A) 的时候,addr 会被链入到 tcachebin,也就是可以分配到 addr

可以看到,和fastbin reverse into tcache的攻击方法很类似,但是得到的效果不一样。TSU可以在任意地址写libc地址,而TSU+除了可以写libc地址,还能再任意地址分配。

1-4 largebin

目前能用的largebin attack只能使用下面这个分支:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* maintain large bins in sorted order */
              if (fwd != bck)
                {
                  /* Or with inuse bit to speed comparisons */
                  size |= PREV_INUSE;
                  /* if smaller than smallest, bypass loop below */
                  assert (chunk_main_arena (bck->bk));
                  if ((unsigned long) (size)
		      < (unsigned long) chunksize_nomask (bck->bk))
                    {
                      fwd = bck;
                      bck = bck->bk;

                      victim->fd_nextsize = fwd->fd;
                      victim->bk_nextsize = fwd->fd->bk_nextsize;
                      fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
                    }
                  else
                    {
                      //......
                  }
                  //......
              }

效果是可以任意地址写堆地址。

largebin attack往往会与其他攻击方法结合起来,因为其写地址的能力,可以修改变量,所以常常用来构造写原语。

1-4-1 house of husk

house of husk方法仍然可以利用,需要找到一个格式化字符串的场景,且打house of husk的时候,至少需要两次格式化字符串。

1-4-2 libc/ld上的变量

libc/ld的地址空间上关键变量非常多,比如_IO_list_allpointer_guardtcache等等。具体的方法会在相关的篇幅里面进行详细说明和补充。

1-5 IO_FILE

1-5-1 house of kiwi

在这个commit里面将__malloc_assert的实现逻辑修改了。

image-20230310102503481

也就是说,在glibc-2.36及其之后,house of kiwi的利用链失效了。

而在这个commit,直接使用默认的assert__malloc_assert被删掉了:

image-20230310102759826

1-5-2 house of emma

只要_IO_cookie_jumps还在,这个方法就能继续使用。但是,由于poniter_guard处于ld的地址空间,所以某些场景是需要爆破的。

1-5-3 house of obstack

glibc-2.36的时候,_IO_obstack_jumps被去掉了,但是还有其他方法可以触发调用链。

glibc-2.37开始这个方法的调用链为:__printf_buffer_as_file_overflow -> __printf_buffer_flush -> __printf_buffer_flush_obstack->__obstack_newchunk

1-5-4 house of apple1/2/3

  • apple1需要和其他技巧结合使用,可以任意地址写堆地址
  • apple2利用的_wide_vtable缺乏校验调用函数指针
  • apple3利用shlib_handle去绕过指针加密调用函数指针

1-5-5 house of lyn/snake

_obstack_newchunk中仍然调用了函数指针,所以还方法仍可以使用。

1-6 _rtld_global

1-6-1 house of banana

整体来看,就是hosue of banana的利用

围绕link_map有很多利用技巧,比如之前有使用格式化字符串修改掉link_map->l_addr,可以让函数解析后的地址被写入到其他地址处。而house of banana的本质也是围绕link_map做利用。

1-7 libc.got

1-7-1 libc.got in IO

比如高版本house of pig没有办法覆写hook指针,因为这些指针都被删掉了,那么可以覆写libc.got项,在IO处理函数中存在着memcpy/memmove等函数,当这些函数被调用的时候会jmp到对应的libc.got存储的地址,因此可以控制libc.got的内容来劫持RIP

1-7-2 libc.got in malloc_printerr

此外,在malloc中的malloc_printerrassert,都会调用到strlengot,因此,在高版本中可劫持该函数的got,来控制RIP

具体来看,就是在__libc_message中有调用strlen

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* Abort with an error message.  */
void
__libc_message (enum __libc_message_action action, const char *fmt, ...)
{
 // ......
      if (cp[0] == '%' && cp[1] == 's')
	{
	  str = va_arg (ap, const char *);
	  len = strlen (str); // 这里调用了strlen
	  cp += 2;
	}
      //.....
  }
}

1-8 heap_info/malloc_state

攻击堆管理中最核心的数据结构,比如有:

  • house of mind伪造heap_info结构体,进而控制arena
  • 直接打掉thread_arena,伪造一个arena
  • 打掉线程的tcache变量
  • 修改pointer_guard

1-9 __environ

  • GLIBC_TUNABLE环境变量的设置会控制ptmalloc_init的流程,影响很多关键变量的设置,比如tcache_counts等。在这里有着设置示例Tunables (The GNU C Library)。比如export GLIBC_TUNABLES=glibc.malloc.tcache_count=2

  • 有些特殊的环境变量会泄露出信息,比如LD_SHOW_AUXV

1-10 other

这里是一些不太好归类的攻击面。有:

  • house of muney,一种steal heap的技巧,通过修改mmap chunksize来达成利用
  • exit的时候会call tls_call_list里面的函数指针,但是也要能控制pointer_guard
  • exit的时候会调用一些锁的函数指针,某些博客中称之为exit_hook,但是在2.34之后这些hook被静态函数所代替

2-参考

[1] Tunables (The GNU C Library)

[2] shellphish/how2heap: A repository for learning various heap exploitation techniques. (github.com)

[3] Overview of GLIBC heap exploitation techniques (0x434b.dev)

[4] [原创] CTF 中 glibc堆利用 及 IO_FILE 总结-Pwn-看雪论坛-安全社区|安全招聘|bbs.pediy.com (kanxue.com)

[5] ptmalloc2 - CTF Wiki (ctf-wiki.org)

Buy me a coffee~
roderick 支付宝支付宝
roderick 微信微信
0%