pwn - Heap Exploitation

今天整理了一下Heap部分的一些笔记,小汇总一下

在程序运行过程中,堆可以提供动态分配的内存,允许程序申请大小未知的内存。堆是程序虚拟空间地址的一块连续的线性区域,由地址向地址上增长

堆题漏洞一般在delete()函数上,多半是指针未清空导致成为野指针,从而可以进行UAF

实现堆利用的方法

  • ptmalloc2 - glibc
  • dlmalloc - General purpose allocator
  • jemalloc - Firefox and FreeBSD
  • tcmalloc - Google
  • libumem - Solaris

ps:与系统交互的函数主要是(s)brk函数以及mmap,munmap函数,不是malloc或者free

malloc

malloc(size_t n)

  • malloc返回对应大小字节的内存块的指针,此外,该函数对一些异常进行了处理
    • n = 0 , 返回当前系统允许的堆的最小内存块
    • 当n为负数时,由于在大多数系统上,size_t是无符号常数,所以会系统会申请很大的内存空间,但通常来说都会失败,因为系统没有那么多的内存可以分配

free

free(void* p)

  • free函数会释放由p指向的内存块,这个内存块可能是通过malloc得到的,也可能是通过相关函数realloc得到的
  • 对异常的处理
    • 当p为空指针时,函数不执行任何操作
    • 当p被释放后,再次释放会出现乱七八糟的效果,即double free
    • 除了被禁用mallopt的情况下,当释放很大的内存空间时,程序会将这些内存空间还给系统,以便于减小程序所使用的内存空间

(s)brk

操作系统提供了brk函数,glibc库提供了sbrk函数,我们可以通过增加brk的大小来向操作系统申请内存

  • 堆的起始地址start_brk和堆的当前末尾brk指向同一地址,根据是否开启ASLR两者的具体位置会有所不同
    • 不开启ASLR:start_brk和brk会指向data/bss段的结尾
    • 开启ASLR:start_brk和brk,也会指向同一位置,只是这个位置会在data/bss段的结尾后随机偏移

chunk

  • 结构其实就是header段+data段
  • size_alignment = 0x10的倍数(malloc 0x18 会拿到 0x20 的大小)
  • 整个memory大小为header(0x10) + data

由malloc申请的内存为chunk,这块内存在ptmalloc内部用malloc_chunk结构体表示,当程序申请的chunk被free掉后,会加入到相应的空闲管理列表

  • chunk结构统一,无论大小,处于分配还是释放状态
  • 根据是否被释放,表现形式会有所不同
字段 释意
prev_sizeimage-20220507005803636 如果该chunk物理相邻的前一个地址chunk(两个指针的地址差值为前一个chunk大小)是空闲的话,那该字段记录的是前一个chunk的大小,否则记录前一个chunk(低地址chunk)的数据
P->PREV_INUSE(P):上一个chunk是否使用中
M->MMAPED(M):chunk是否通过mmap生成
NON_MAIN_AREA(N):该chunk是否不属于main area
size chunk的大小必须是2*SIZE_SZ的整数倍;32位SIZE_SZ大小为4,64位SIZE_SZ大小为8
fd chunk处于分配状态,fd开始是用户数据;若chunk空闲,则fd指向下一个(非物理相邻)的空闲chunk.,bk指向上一个(非物理相邻)的空闲chunk;通过fd,bk将空闲的chunk块加入到空闲的chunk块链表进行统一管理

bin

用户释放掉的chunk不会马上返回给系统,会由ptmalloc管理heap和mmap映射区域中空闲的chunk,ptmalloc管理器会挑一块适合的给用户,这样可以避免频繁的系统调用,降低内存分配的开销

根据chunk的大小和使用状态将chunk初步划分为4类:fast bins,small bins,large bins,unsorted bins

除了fast bin是单链表,其余(small bin,large bin,unsorted bin)都是双链表

堆溢出

程序向堆块中写入的字节数超过了堆块本身可使用的字节数(可利用的字节数都不小于用户申请的字节数),因而导致了数据溢出,并覆盖到物理相邻的高地址的下一个堆块

Fast Bin

  • chunk的大小在32字节-128字节(0x20 - 0x80)的chunk称为fast chunk(大小为内存中struct_malloc_chunk的大小)
  • Fastbin链表个数为10个
  • 快速地进行小内存的分配和释放
  • 单链表(使用fd指针,bk没有用到),故fastbin中增添或者删除fast chunk都是在对链表尾进行操作
  • fastbin链表的每个chunk大小相同,不同链表之间的chunk不同
  • LIFO(Last In First Out)
  • 被free掉的时候会将下一块chunk p设置成为0

Unsorted Bin

  • 释放较小或较大的chunk时,如果系统没有将他们添加到对应的bins中,系统就会将这些chunk添加到unsorted bin中去
  • 为了glibc mallc机制第二次机会利用最近释放的chunk(第一次是fastbin),加快内存的分配和释放,不再花费额外的时间去查找额外的chunk
  • Unsortedbin链表个数为1个
  • free chunks组成的循环双链表
  • 对大小没有限制
  • FIFO

Small Bin

  • 小于1024字节(0x400)的chunk称之为small chunk,small bin用于管理small chunk
  • smallbin链表个数62个
  • 相邻的free chunk需要进行合并操作,合并成一个大free chunk
  • 内存分配速度:fas tbin < small bin < large bin
  • free chunks组成的循环双链表
  • FIFO
  • small bin链表中每个chunk大小相同,不同链表之间chunk大小不同
  • free:small chunk free之后,会检查该chunk相邻的chunk是否free,如果是的话将进行合并操作,将这些chunks合并成为新的chunk,然后将它们从small bin中移除,最后将新的chunk添加到unsorted bin中,之后unsourted bin进行整理再添加到对应的bin链上

Large Bin

  • 大小等于1024字节(0x400)的chunk称为large chunk
  • largebin链表个数为63个,分为6组
  • 用fd_nextsize , bk_nextsize连接起来
  • 其合并类似于small bin
  • 同一个largebin链表每个chunk可以不一样(从大到小排序)
  • large chunk可以添加,删除在large bin中的任何一个位置

Unsorted Bin

  • 双向链表(Circular doubly linked list)
  • free的chunk size > fastbin时,不会直接放到对应的bin里,会先丢到Unsorted Bin
  • malloc fastbin size大小时会先去fastbin list里找,若没有则会到unsorted bin找,如果找到一样大小的则回传,若无但找到大小大于所需大小的chunk则会切割回传,剩下的部分都会丢回unsorted bin,最后实在没有会从top chunk切出来回传