• Dance In Heap(二):一些堆利用的方法(上)


    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
    ");
    }
    
    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。

  • 相关阅读:
    巴洛克式和哥特式的区别
    推荐阅读书籍,是时候再行动起来了。
    AtCoder ABC 159F Knapsack for All Segments
    AtCoder ABC 159E Dividing Chocolate
    AtCoder ABC 158F Removing Robots
    AtCoder ABC 158E Divisible Substring
    AtCoder ABC 157F Yakiniku Optimization Problem
    AtCoder ABC 157E Simple String Queries
    AtCoder ABC 157D Friend Suggestions
    AtCoder ABC 156F Modularness
  • 原文地址:https://www.cnblogs.com/h2zZhou/p/7741687.html
Copyright © 2020-2023  润新知