zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    对ManualResetEvent和AutoResetEvent的巩固练习
    经纬度点距离的那点儿事
    【读书笔记】C++Primer---第三章
    .NET应用程序调试—原理、工具、方法
    【读书笔记】C++Primer---第二章
    【读书笔记】C++Primer---第一章
    8 个最好的 jQuery 树形 Tree 插件
    C++中引用(&)的用法和应用实例
    自娱自乐之直接插入排序
    自娱自乐之堆排序
  • 原文地址:https://www.cnblogs.com/h2zZhou/p/7741687.html
Copyright © 2011-2022 走看看