MindshaRE:使用Binary Ninja API检测潜在释放后使用漏洞
释放后使用(Use-after-Free)是一种内存破坏情况,程序在内存被释放回分配器后仍引用该内存。静态检测这类漏洞具有挑战性。本文探讨了如何使用Binary Ninja的中级中间语言(MLIL)通过追踪特定内存分配与其他内存区域的交互来建立数据流图,并基于该图进行跨函数的上下文不敏感可达性分析,以识别二进制文件中的潜在UAF漏洞。
构建内存分配的数据流图
数据流信息以图形化方式呈现,其中:
- 节点代表不同的内存区域
- 边代表在这些区域之间建立关系或交互的指针存储操作
实现中使用四种节点类型:
- 跟踪分配节点(红色):代表感兴趣的内存分配,是跟踪图中交互的焦点
- 函数栈帧节点(绿色):代表过程间分析期间访问的各个函数的栈帧
- 动态内存节点(蓝色):代表无法绑定到特定源的SSA变量
- 全局内存节点(黑色):帮助分析单个函数内的交互
图中的边代表指针存储操作,建立内存分配之间的连接。源节点对应正在写入的内存,目标节点代表被存储的指针值。边属性捕获从分配基地址的偏移量。
映射SSA变量、图节点和边之间的关系
在自动化分析中,第一个要跟踪的SSA变量是被分配调用返回值(如malloc()或calloc())的变量。利用定义使用链遍历函数内的所有使用情况。
当变量赋值涉及指针运算时,除了节点信息外还会存储偏移信息。内存存储操作(如MLIL_SET_VAR_SSA、MLIL_STORE_SSA或MLIL_STORE_STRUCT_SSA)会在图中创建边。
将内存加载转换为图边
函数范围外的内存加载操作也表示为图边。假设内存被加载,则必须事先已初始化。内存存储、赋值和加载操作是数据流图的基本构建块。
遍历数据流图以传播信息
通过SSA变量字典和先前初始化的图来传播信息:
- 直接变量赋值很简单
- 涉及指针运算的变量赋值会存储偏移信息
- 从内存加载数据的变量赋值会访问图的边来获取目标节点
记录与跟踪分配相关的指令
完成SSA变量映射和数据流图生成后,重新访问指令。记录所有依赖于跟踪分配节点的内存加载、内存存储或调用指令,视为"使用"。同时记录涉及释放器函数的调用指令,视为"释放"。
通过调用栈进行过程间UAF检测
检测潜在UAF漏洞涉及分析所有分类为"释放"的基本块,并验证是否有路径通向分类为"使用"的基本块。如果存在此类路径,则标记为潜在UAF条件。
自动检测分配器和释放器调用
虽然理想情况是使用程序特定的分配器和释放器包装器,但手动识别它们可能具有挑战性。更简单的起点是输入标准函数如malloc()、realloc()和free(),检查结果,并根据结果逐步完善分析。
分析过去的真实漏洞
通过测试已知易受攻击的程序来了解工具的工作原理,包括:
- CVE-2015-5221: JasPer JPEG-2000
- CVE-2016-3177: Giflib
- GNOME-Nettool中的UAF漏洞
- CVE-2015-5177: OpenSLP中的双重释放问题
结论
本文介绍了使用Binary Ninja通过数据流分析和图可达性来发现释放后使用漏洞的方法。该实现虽然存在分类错误,但提供了可用于建模其他类型漏洞的基本原理。
致谢和参考文献
- Trail of Bits关于Binary Ninja的各种博客文章
- Josh Watson的使用Binary Ninja的各种项目
- Jordan的代码片段和Binary Ninja slack社区
- Josselin Feist的GUEB静态分析器
- Sean Heelan关于使用静态分析发现释放后使用漏洞的工作