来源会议:DIMVA 2020,虽然是个C会,但文章内容挺长的,有21页单栏。
简介
采用符号执行和专家知识结合的方法,针对CTF程序的堆漏洞自动化利用的文章。考虑的防御机制包含NX和Full RELRO。使用到的信息包含PoC,漏洞程序和先验专家知识。
2个贡献:
- 利用很少能力的堆漏洞
- 对程序和堆分配器进行建模
方法
分为三个步骤:
- 堆操作建模
- 漏洞分析
- 利用生成
堆操作建模
生成控制流图,标记堆操作的函数调用。主要对三类原语进行标记:
- allocation:分配
- deallocation:释放
- edit:编辑
通常来说,堆原语主要分为三种:
- 与堆分配器相关的函数调用,比如malloc、calloc、free等等
- 将堆指针作为参数的函数调用,比如read、fgets等等
- 将堆对象作为目标地址的内存写指令。
为了分析堆原语,HEAPG使用符号执行执行函数路径。将输入字节符号化,并且追踪与堆区域交互的指令。HEAPG并不会追踪共享库。如果任何堆原语的属性是符号化的,HEAPG会去求解并且记录范围,而不是具体化。为了推断堆原语的关系,还给每个堆指针贴了一个全局标签。
漏洞分析
从AFL获取崩溃输入并且通过检测内存使用的违反情况来分析漏洞的能力。HEAPG动态地执行poc,并且将指针污点化。然后传播到指向的内存区域。
另外,还给堆对象加标签。一开始标签是uninitialized,当有指令写对象的时候,标签会变为busy,当被释放时则是free。同时也记录heap object的大小。如果任何指令访问了堆的内存,我们可以知道指针的标签。
根据下面的表格,判断是哪种类型的堆漏洞。
基于模板的利用生成
这个部分分为模板、攻击序列生成、攻击序列评估这几个部分。
模板主要包含3个组件(能否自动生成该模板呢?):
- 原语序列:堆原语的顺序。比如循环释放fastbin的堆块。
- 布局约束:比如,unsafe unlink需要victim 堆块分配unsorted bin的大小,并且伪造的堆块需要在victim堆块的旁边。
- 要求:要使用一个利用技术,需要有些前置条件。比如fastbin攻击需要对象大小是fastbin size。
攻击序列生成和提供的模板关系密切。首先,检查目标程序是否满足要求,比如漏洞类型和glibc的版本号。如果满足要求,则选择某个模板开始下一步。
在二进制程序中搜索函数路径,去找到模板中的backbone 原语。并将函数路径和原语放在一起构成攻击序列。对于如何执行攻击序列,又提出了两种方法:
- 堆模拟器:使用它来执行堆原语,并且模拟目标程序的intermediate state。
- 符号执行:让目标程序执行攻击序列的函数路径,并且将堆原语作为数据约束。
攻击序列评估
根据模板的布局约束来评估攻击序列。动态执行目标程序和攻击序列,同时记录运行时的堆布局。然后提取每个堆对象的地址,真实的大小和存储对象指针的地址。基于这些信息,就基本完成了backbone primitive。
除此之外,还会去检查当前的堆布局是否满足布局约束条件,包括堆对象的距离和状态。为了提高性能,先使用堆模拟器来快速评估。只有攻击序列通过了评估,HEAPG才会使用S2E去精准评估。
如果满足了布局约束,程序会进入可利用的状态,并且HEAPG试图去生成一个exploit。否则,HEAPG会找到有冲突的布局。然后回推断冲突的原因,并试图修正它。通常来说,冲突的原因有以下几个:
- Heap chunk A需要释放,但是处于busy或者未初始化状态
- Heap chunk A和B需要相邻,但是没有其他的busy块在他们之间
- Heap chunk A和B需要相邻,但是A的相邻块被释放了。
一旦攻击序列通过了评估,HEAPG就会使用符号执行,并且通过追踪目标程序的指令来检查利用原语,同时检查符号数据是否在以下地址中:
- 内存写指令和函数调用的内容和目标地址
- 非直接跳转的目标地址
- bin的head指针
- 函数指针的值
基于以上符号数据的位置,就可以使用下面的原语去生成一个利用的输入。
- 任意代码执行:如果函数指针或者非直接跳转的地址被覆盖了,就可以覆盖这些指针为一个任意的值。从而实现任意代码执行。
- 任意地址写:内存写指令或者函数调用的地址是符号化的,就可以写任意的数据到任意的位置。
- 任意分配:如果bin的头指针是符号化的,那说明可以分配一个堆块到任意的一个位置。
结果
使用CTF程序作为评测,程序来自ctftime.org,pwnable.tw和github.com。选择程序的标准:
- 至少有个堆漏洞
- 难度系数越高越好
写了四个利用模板,包含fastbin,unsafe unlink,house of force和tcache poisoning。
从有效性,性能两方面进行评估。
有效性
大部分程序都能生成利用。
性能
不能生成利用有几个原因:
- 不能找到利用原语去存储非法的堆对象
- 现有的模板不支持house of autumn
- 缺少能用的模板,比如不能处理children_tcache,iz_heap_lv2和schmaltz
- 不能检测到漏洞,由于是在对象级别追踪堆操作,所以不能处理堆对象内的数据越界。
- 程序分析的问题。比如带有AES加密算法的程序、混淆过的程序,这些都会导致状态爆炸。
讨论
整体的局限性,有以下三点:
- 不能处理复杂程序
- 不完整。如果没有补充利用模板,无法实现未知的利用技术。
- 不能用在ptmalloc以外的堆分配器的程序
总结
这篇文章的思路不错,挺简单易懂。要是开源就好了。
虽然有不少局限性,但也说明这个领域还有不少待解决的问题。