如何利用Unsorted Bin泄露Libc基地址

Posted on Feb 16, 2021

这其实是一个很简单很枯燥的问题,许多题中都会用到,但是我发现好像并没有文章以该方法为主角介绍该方法,也没有对其原理做出详细解释。估计是大佬们都觉得这个问题太简单了不愿意费时间来写,而回想过去,做题时总是会记得有这样一个利用方法,但是却找不到具体的利用原理。所以我就来细讲一下这个问题。

Unsorted Bin 在管理时为循环双向链表,若 Unsorted Bin 中有两个 bin,那么该链表结构如下

graph LR
A("main_arena.bins[0,1] ___") -->|"fd"|B("bin1")
B -->|bk|A
B -->|"fd"|C("bin2")
C -->|"bk"|B
C -->|"fd"|A

下面这张图就是上面的结构的复现

我们可以看到,在该链表中必有一个节点(不准确的说,是尾节点,这个就意会一下把,毕竟循环链表实际上没有头尾)的 fd 指针会指向 main_arena 结构体内部。而 main_arena 是一个struct malloc_state 类型的全局变量,是 ptmalloc 管理主分配区的唯一实例。说到全局变量,立马可以想到他会被分配在 .data 或者 .bss 等段上,也就是说是编译时分配,那么如果我们有进程所使用的 libc.so 文件的话,我们就可以获得 main_arenalibc 基地址的偏移,实现对 ASLR 的绕过。

那么如何取得 main_arenalibc 基址的偏移呢?这里提供一种思路,也是许多师傅使用的方法。

malloc.c 中有这样一段代码

int
__malloc_trim (size_t s)
{
  int result = 0;

  if (__malloc_initialized < 0)
    ptmalloc_init ();

  mstate ar_ptr = &main_arena;//<=here!
  do
    {
      __libc_lock_lock (ar_ptr->mutex);
      result |= mtrim (ar_ptr, s);
      __libc_lock_unlock (ar_ptr->mutex);

      ar_ptr = ar_ptr->next;
    }
  while (ar_ptr != &main_arena);

  return result;
}

注意到 mstate ar_ptr = &main_arena; 这里对 main_arena 进行了访问,所以我们就可以通过 IDA 等工具分析出偏移了。

比如把 .so 文件放到 IDA 中,找到 malloc_trim 函数,就可以获得偏移了。

利用的方法

一般来说,要实现 leak,需要有 UAF,将一个 chunk 放入 Unsorted Bin 中后再打出其 fd。一般的笔记管理题都会有 show 的功能,对处于链表尾的节点 show 就可以获得 libc 的基地址了。

特别的,CTF 中的利用,堆往往是刚刚初始化的,所以 Unsorted Bin 一般都是干净的,当里面只存在一个 bin 的时候,该 binfdbk 都会指向 main_arena 中。

另外,如果我们无法做到访问链表尾,但是可以访问链表头,那么在 32 位的环境下,对链表头进行 printf 等往往可以把 fdbk 一起输出出来,这个时候同样可以实现有效的 leak。然而在 64 位下,由于高地址往往为 \x00,很多输出函数会被截断,这个时候可能就难以实现有效 leak。