二进制安全之——堆相关漏洞

发布于:2022-11-28 ⋅ 阅读:(311) ⋅ 点赞:(0)

目录

 一、堆

1.堆的介绍

2.堆的基本数据结构chunk

(1)mallco_chunk数据结构

(2)free chunk

(3)top chunk

(4)N,M,P位

3.堆空闲块管理结构bin

(1)fast bin

(2)其他bin

①bin 1

②bin 2-bin63

③bin64-bin126

4.malloc基本规则

5.free基本规则

6.tcache

(1)tcache的管理结构

tcache_entry结构体

 tcache_perthered_struct结构体

(2)tcache的管理函数

二、漏洞类型

1.基本的堆漏洞

2.堆缓冲区溢出

(1)常规堆缓冲区溢出

①覆盖堆块内部数据

②覆盖后续堆块数据

(2)off by one

3.use after free

4.double free

三、堆喷射


 一、堆

1.堆的介绍

主要是指用户动态申请的内存(如:调用malloc,alloc,alloca,new等函数)。glibc malloc源码中有三种最基本的堆块数据结构,分别是heap_info,malloc_state,malloc_chunk。

2.堆的基本数据结构chunk

在glibc中,chunk是内存分配的基本单位,可分为alloced chunk,free chunk,top chunk,last remainder chunk四种,最后一种用的比较少,这里着重介绍前三种。

(1)mallco_chunk数据结构

① pre_size/pre_data

前一堆块释放(P=0):前一堆块大小

前一堆块使用(P=1):前一堆块数据

②size

本chunk大小,size为8的整数倍,所以后三个字节有特殊用途

内存中,堆块对齐规则如下:

x32中,按0x8字节对齐,chunk最小为0x10字节;x64中,按0x10字节对齐,chunk最小为0x20字节

(2)free chunk

空闲堆块,其中的fd,bk属于链表指针,有特定的含义,下一堆块的pre_size为当前释放块的大小(含头部信息),下一堆块size域中的p标志通常会被设为0(存在例外,如fast bin)

(3)top chunk

空闲块,该块位于前两种堆块之后,其堆块结构与alloced块的结构类似,重要的是pre_size域和size域。

(4)N,M,P位

N位:#define NON_MAN_ARENA 0x4.用于表示是否属于主线程,0表示主线程的堆块结构,1表示子线程的堆块结构

M位:#define IS_MMAPPED 0x2。用于表示是否由mmap分配,0表示堆块中的top chunk分裂产生,1表示由mmap分配

P位:#define PREV_INUSE 0x1.用于表示上一堆块是否处于空闲状态,0表示处于空闲状态,1表示处于使用状态(被分配),此位主要用来判断free时是否能遇上一堆快进行合并。不过,也存在例外情况,如,fastbin,为了满足快速分配小内存的需求,通常fastbin中的p位保持为1,不参与合并。

3.堆空闲块管理结构bin

当alloced chunk被释放后,会放入bin或者合并到top chunk中去。bin的主要作用是加快分配速度,其通过链表方(chunk结构体中的fd和bk指针)管理

(1)fast bin

fast bin中包含一维指针数组头,用于管理小堆块,在x64系统中,保存的堆块大小在0x20-0x80之间;在x32位系统中,其大小区间0x10-0x40(x86),该区间两边的值都包含在内。fast bin按单链表结构进行组织,用fd指针指向下一堆块,采用LIFO机制,在fast bin中,堆块的p标志都为1,处于占用状态,以防止释放时对fast bin进行合并,用于快速分配小内存

(2)其他bin

其他bin包含一维指针数组头,其按双链表结构进行组织,采用FIFO机制,不同大小的堆块,通常链接在不同的指针数组里面。具体如下:

①bin 1

unsorted bin。主要用于存放刚刚释放的堆块以及大堆块分配后剩余的堆块,大小没有限制

②bin 2-bin63

small bin。主要用于保存在0x10-0x400(x86,对于x64是0x20-0x800)区间的堆块,同一链表中堆块的大小相同,如x86下bin2对应0x10,bin3对应0x18.....

③bin64-bin126

large bin。主要用于存档大小大于0x400(x86,对x64是0x800)的堆块,同一链表中堆块打大小不一定相同,在一定范围内,按照从小到大的顺序进行排列。

4.malloc基本规则

malloc基本规则可以对照malloc的源码中的_int_malloc函数来查看,这里主要介绍最基本的情况,最开始glibc所管理的内存空间是用brk系统调用产生的内存空间,如果malloc申请的空间太大,超过了现有的空闲内存,则会调用brk或者mmap继续产生内存空间。

对于malloc申请一般大小的内存,其简化流程是:

x86:size+4按照0x10字节对齐,x64:size+8按照0x20字节对齐

(1)检查size_real是否符合fast bin的大小,若是则查看fast bin中对应的size_real的那条链表中是否存在堆块,若是则分配返回,否则进入第2步

(2)检查size_real是否符合small bin的大小,若是则查看small bin中对应size_real的那条链表是否存在堆块,若是则分配返回,否则进入第3步

(3)检查size_real是否符合large bin的大小,若是调用malloc_consolidate函数对fast bin中所有的堆块进行合并,其过程为将fast bin中的堆块取出,清楚下一块的p标志位进行堆块合并,将最终的堆块放入unsorted bin。然后small bin和largr bin中找到适合的size_real大小的块,若找到则分配,并将多余的部分放入unsorted bin,否则进入第4步

(4)检查top chunk的大小是否符合size_real的大小,若是则分配前面一部分,并重新设置top chunk,否则调用malloc_consolidate函数对fast bin中的所有堆块进行合并,若依然不够,则借助系统调用来开辟新的空间进行分配,若还是无法满足,则在最后返回失败。

注意:

fast bin的分配规则是LIFO

malloc_consolidate函数调用的时机:它在合并时会检查前后的块是否已经释放,并触发unlink

5.free基本规则

堆块在释放的时候会有一系列的检查,可以与源码进行对照。

(1)释放(free)时首先会检查地址是否对齐,并根据size找到下一块的位置,检查其p标志位是否为1

(2)检查释放块的size是否符合fast bin的大小区间,若是,则直接放入fast bin,并保持下一堆块中的p标志位为1不变,否则进入第三步。

(3)若本堆块size域中的p标志位为0(前一堆块处于释放状态),则利用本块的pre_size找到前一堆块的开头,将其从bin链表中摘除(unlink)并合并这两个块,得到新的释放块

(4)根据size找到下一堆块,若是top chunk,则直接合并到top chunk中去,直接返回,否则检查后一堆块是否处于释放状态(通过检查下一堆块的一下堆块的p标志位是否为0)。将其从bin链表中摘除(unlink),并且合并这两块,得到新的释放块

(5)将上诉合并得到的最终堆块放入unsorted bin中去

最基本的unlink定义如下:

 上述代码直接进行如下赋值:

6.tcache

tcache是libc2.26之后引进的一种新的机制,广泛应用于18.04之后的系统,其管理方式类似于fast bin,每条链上最多可以有7个chunk,只有tcache满了之后,chunk才会被放回其他链表,而在进行malloc操作时,tcache会被首先分配

(1)tcache的管理结构

tcache的两个重要结构体为tcache_entry和tcache_perthered_struct,具体如下:

tcache_entry结构体

 tcache_perthered_struct结构体

 其中TCACHE_MAX_BINS默认值为64,堆空间起始部分都会有一块先于用户申请分配的堆空间,大小为0x250

(2)tcache的管理函数

tcache的两个重要的管理函数为tcache_get()和tcache_put(),分别用于获取tcache和释放tcache,代码如下:

 释放tcache的代码如下:

 tcache的主要作用是提高堆的使用效率,上述两个函数会在堆函数_int_free和_libc_malloc的开头被调用,对于tcache,需要了解如下几个关键点:

①tcache是管理单链表,采用LIFO原则

②tcache的管理结构存在于堆中,默认有64个entry,每个entry最多存放7个chunk

③tcache的next指针指向chunk的数据区(与fast bin不同的是,fast bin指向chunk头)

④tcache的某个entry被占满以后,符合entry大小的chunk被free后的规则和原有机制相同

二、漏洞类型

1.基本的堆漏洞

基本的堆漏洞主要是由于对堆内容的类型判断不明而形成的错误引用。通常情况下,可以使堆块来存储复杂的结构体,其中可能会包括函数指针、变量、数组等成员。如果一个结构体的数据按照其他结构体格式来解析,那么只要在特定的域布布置好数据,就会导致漏洞的发生。

2.堆缓冲区溢出

(1)常规堆缓冲区溢出

堆缓冲区溢出与栈缓冲区溢出类似,是指在堆上的缓冲区被填入了过多的数据,超出了边界,导致堆中原有的数据被覆盖。分为两种情况:

①覆盖堆块内部数据

通常发生在结构体中,如果结构体中的数组溢出,则覆盖后续变量

②覆盖后续堆块数据

不仅影响了后续堆块中的数据,也破坏了堆块结构。

(2)off by one

在堆缓冲区溢出中,有一种比较特殊的情况,只能溢出1字节,称为off by one。这种情况下多出现于末尾,溢出的1字节恰好能够覆盖下一堆块的size域的最低位,这种漏洞可能比较难利用。

3.use after free

use after free(UAF)即释放后使用漏洞。若堆指针在释放后未被置空,形成悬挂指针(野指针),当下次访问该指针的时候依然能够访问到原指针所指向的堆内容,就会形成漏洞。UAF漏洞的利用需要根据情况来进行分析,以判断其是否具有信息泄露和信息修改的功能。

4.double free

double free漏洞主要是指对指针存在多次释放的情况,是UAF中较为特殊的一种,针对的是用于释放的函数,多次释放能够使堆块发生重叠,前后申请的堆块可能会指向同一块内存,这种情况下,可将其转化为最基本的堆问题,另外,还可以构造特殊的堆结构,从而运用针对堆块结构的利用方法。

三、堆喷射

堆喷射主要是指,在堆块中布置好大量重复的数据,便于目的地址索引到推上的数据。堆喷射相当于栈喷射来讲较为麻烦。

通常简单的堆喷射可以类比栈喷射,堆喷射中有专门针对随机申请隔离堆的保护机制。堆喷射可以使随机申请的堆大小与所使用的堆大小相同,若存在UAF漏洞,则能够产生利用

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到