-
-
[原创]Glibc Heap 利用之初识 Unlink
-
发表于:
2018-11-20 20:53
11689
-
[原创]Glibc Heap 利用之初识 Unlink
在 Glibc 管理堆的过程中,无论一个内存块(chunk)是处于已分配状态还是处于空闲状态,Glibc 都统一使用一个名为 malloc_chunk 的结构体对其进行描述(可以将其理解为 chunk 的 header),下图简单描绘了 chunk 在堆中的一个布局:
而关于 malloc_chunk 的具体内容,我们可以查阅 Glibc源码中的 mallo.c 文件,如下所示:
malloc_chunk 中的各个字段对于已分配的块和空闲的块而言,是有着不同含义的:
空闲的 chunk 所对应的 malloc_chunk 结构体由 glibc 的内存管理器 ptmalloc 所管理,ptmalloc 会根据它们的大小和使用状态将它们保存到互不相关的链表中,而它们在堆中的结构大概是下面这样子的:
已分配的 chunk 在堆中的结构则是这个样子:
简单来说,unlink 就是一个被 ptmalloc 用来提取双向链表(指上文中通过 chunk 头管理堆中空闲 chunk 的链表)中空闲 chunk 的操作。它的基本流程如下图所示:
上图所示的过程,其实可以用下面几行代码来表示:
不难看出,这样的操作是有一定风险的,倘若攻击者利用堆溢出覆盖了 unlink 对象的 fd
指针和 bk
指针,便会造成任意地址读写。在古老的 unlink 中的确有这个问题存在,因为它没有对 unlink 对象的相关字段进行检查,也就是说,它并没有下面代码中被注释掉的那部分内容:
但即使是增加了对相关字段的检查,unlink 也不是绝对安全的,现在已经有不少绕过这些检测的方法,本文暂时不对这方面内容进行讨论,下面我们通过一道题来了解下古老 unlink 的利用方式。
这道题可以说是很好地复现了古老的 unlink 操作(题目链接),很适合用来了解 unlink 的原理,下面来分析分析它:
先看题目源码,栈的地址和堆的地址皆已给出,利用点也很明确,也就是 gets(A->buf)
和 unlink(B)
这两个地方,所以利用流程大致上可以归结为:先通过堆溢出覆盖 B 的相关字段,再通过 unlink 函数实现任意地址读写,从而将主函数的返回地址写为 shell 函数的起始地址。
在第一时间,很多人通常会想到将 A 和 B 构造成下面这个布局(padding 的大小之所以为16个字节,是因为32位系统下 chunk 的大小最小不低于16),然后通过 FD->bk=BK
将 shell 函数的起始地址写到返回地址上。但是这样做的话,到下一步执行 BK->fd=FD
时,程序会尝试向代码段上写入数据,这种非法写入将会引发错误,导致程序无法继续执行。
既然遇到了瓶颈,那我们不妨去分析一下反汇编后的 main 函数,寻找新的突破口。最终在 main 函数的末尾,发现了可利用的地方,如下所示:
于是我们可以把 A 和 B 的布局构造成下面这样(关于偏移量,可以通过分析汇编代码或者动态调试取得,这里就不多讲),这样构造既不会出现非法写入的情况,又使得我们可以借助 BK->fd=FD
让程序转去执行 shell 函数。
最终的利用代码如下所示:
本文部分内容参考于:
https://github.com/bminor/glibc/blob/master/malloc/malloc.c
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/unlink/
https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/
文章若有不足和错误之处,望各位读者指正!!!
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2019-1-31 17:28
被kanxue编辑
,原因: