0×00 前面的话

在前面的文章里我们稍微有点啰嗦的讲解了堆中的一些细节,包括malloc、free的详细过程,以及一些检查保护机制,那在这篇文章里,我们就开始结合这些机制,以64位为例来看一看如何对堆进行攻击。本篇文章稍微讲解了一下UAF漏洞,然后根据源码分析了一下哪些地方使用了 unlink 宏,将unlink漏洞与其他的 chunk 释放操作做了一下区分并分析了unlink漏洞,最后讲解了另外一种利用 chunk 从 bin 中释放但不同于unlink的漏洞 unsortedbin attack。

本篇文章目录

0x01 Use After Free
0x02 Unlink
0x03 unsortedbin attack
0x04 小结

0×01 Use After Free

要学习堆中的漏洞,最基础不过的就是这个 UAF 了,UAF 漏洞原理很简单,就是在 free 掉 chunk 后,指向该 chunk 的指针还能正常使用

#include<stdio.h>
#include<stdlib.h>
struct shell {
void (*getshell)();
};
struct data {
int data;
};
void test_getshell(){
printf("I get the shell\n");
} int main () {
struct shell *p;
p = (struct shell*)malloc(sizeof(struct shell)); p->getshell = test_getshell;
free(p);
struct data *q;
q = (struct data*)malloc(sizeof(struct data));
q->data = 1234;
p->getshell();
return 0;
}

编译运行一下

Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000000004d2 in ?? ()

我们可以看到 p 指向的函数地址被我们用1234给替换掉了,这就意味着我们能够利用这样一个漏洞控制 rip 寄存器,执行指令。

0×02 unlink

unlink漏洞想必大家都不陌生,在前面我们提到过,系统通过 unlink 宏将 free chunk 从链表中取出,但是我在这里强调一下,并非所有从链表中取出 chunk 的操作都利用到了 unlink 宏,要知道,我们在 malloc 时,也多次将 chunk 从 bin 中取出,我想结合部分源码(只截取了取出部分的代码)来强调一下 unlink 的使用状况。

在 malloc 操作中,我们多次进行了 bin 之间的转移,具体如下

  1. 从 fastbin 中取出 chunk

    mfastbinptr* fb = &fastbin (av, idx);
    victim = *fb;
    *fb = victim->fd;
  2. 从 unsortedbin 中取出 chunk

    victim = unsorted_chunks(av)->bk
    bck = victim->bk;
    unsorted_chunks(av)->bk = bck;
    bck->fd = unsorted_chunks(av);
  3. 从 unsortedbin 向 smallbin 转移 chunk

    if (in_smallbin_range(size)) {
    victim_index = smallbin_index(size);
    bck = bin_at(av, victim_index);
    fwd = bck->fd;
  4. 从 unsortedbin 向 largebin 转移 chunk

    mark_bin(av, victim_index);
    victim->bk = bck;
    victim->fd = fwd;
    fwd->bk = victim;
    bck->fd = victim;
  5. 从 smallbin 中取出 chunk

    idx = smallbin_index(nb);
    bin = bin_at(av,idx);
    victim = last(bin);
    bck = victim->bk;
    bin->bk = bck;
    bck->fd = bin;
  6. 从 largebin 中取出 chunk

    unlink(victim, bck, fwd);
  7. 合并 fastbin 中 chunk 并加入到 unsortedbin 中(单向链表,bk指针需要获取)

    prevsize = p->prev_size;
    size += prevsize;
    p = chunk_at_offset(p, -((long) prevsize));115
    unlink(p, bck, fwd);
    ......
    size += nextsize;
    unlink(nextchunk, bck, fwd);

    我们发现不仅仅在 free 时进行向前向后合并时使用 unlink 宏,在 malloc 时也会有零星的 unlink 使用,而且一定要注意,上面的除了6、7外,在进行取出 chunk 操作时,并没有进行 unlink,所以在对这一部分进行漏洞利用时,不需要考虑 unlink 的检查。

现在言归正传,来看看 unlink 漏洞。

#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr (check_action, "corrupted double-linked list", P);
else {
FD->bk = BK;
BK->fd = FD;

上面所示是 unlink 宏的主要实现,我们现在设想申请两个chunk,并利用第一个chunk溢出到第二个chunk的size位,将第一个chunk的 inuse 位改写为 free状态,这时候我们再free第二个chunk,此时系统通过第二个chunk的size检查第一个chunk,发现他是free状态,那么这时候就会使用unlink将第一块chunk从bin中释放出来并与第二块合并

a = malloc(0x20)
b = malloc(0x20) // b.size = 0x20 + chunk_header | inuse(0x01) == 0x31
a[0x20+4] = 0x30 // 覆盖 inuse 位
free(b) // 检查inuse位,发现 a 为 free,执行 unlink,合并两个 chunk

这时候其实a并没有在 bin 中,但是如果我们对 a 的前两个元素(即”fd”、”bk”)进行构造,那么就可以造成任意地址写入。

首先我们需要绕过检查,我们进行一个小小的计算

    P->bk->fd == P // [P+0x18]+0x10 == P [P+0x18] == P-0x10
P->fd->bk == P // [P+0x10]+0x18 == P [P+0x10] == P-0x18

发现我们只需要在 b 的”fd”指针处放入 b-0×18,在”bk”指针处放入 b-0×10,即可绕过检查

执行完 unlink宏后,我们的b变成了这样

    FD->bk = BK   *P = P - 0x10(0x8)
BK->fd = FD *P = P - 0x18(0xc) // 这一步覆盖上一步

也就是说 现在 b 处存放着 b-0×18 的地址,这时候我们再向 b 写入数据也就是向 b-0×18 处写入数据了

--------|-------|
b | |----
--------|-------| |
····· | | |
--------|-------| |
b-0x18 | |<---
--------|-------|

这时候我们通过两次写入来造成任意地址写入,第一次写入0×18个字节,最后几位放入要写入的地址

--------|-------|      |-------|
b |address|----> | 写入 |
--------|-------| |-------|
····· | |
--------|-------|
b-0x18 | AAAA |
--------|-------|

我们再次写入时,就是修改该地址处的数据了,比如修改got表什么的

0×03 unsortedbin attack

对 unsortedbin 的攻击主要利用从 unsortedbin 中取出 chunk 的操作来进行向任意位置写入一个不可控的指针,注意这里,从unsortedbin链表中取出chunk并不是使用unlink宏,所以不需要绕过 unlink 检查。首先我们需要创建两个 chunk 来避免 free 第一个 chunk 时将该 chunk 并入 top chunk,并且第一个 chunk 要足够大,确保其能进入到 unsortedbin中

p = malloc(0x400)
malloc(0x200)

然后将 p free掉,此时 p 进入到 unsortedbin中,然后改写其的 bk 指针,并malloc

free(p)
p[1] = 0xdeadbeef-0x10 // 任意地址 - 0x10
malloc(0x400)

我们看一下从 unsortedbin 中取出 chunk 的操作

victim = unsorted_chunks(av)->bk // victim为free掉的p
bck = victim->bk; // bck 为 任意地址 -0x10
unsorted_chunks(av)->bk = bck; // 调整链表
bck->fd = unsorted_chunks(av); // 任意地址 -0x10 + 0x10 = unsortedbin

这个漏洞自由度较小,不过可以用来修改一些阈值,例如更改libc中的max_fast,从而使得任意分配都使用fastbin来实现,为其他漏洞提供方案。

0×04 小结

在这次的文章中,我们简单的讲解了一下UAF漏洞,然后主要对从 bin 中释放 chunk 时的操作进行了漏洞利用,包括经典的 unlink,当然我们也结合源码分析了一下它的使用状况,以及非unlink式的chunk释放,unsortedbin attack。

05-14 21:35