释放后使用漏洞的静态检测挑战
释放后使用(Use-after-Free)是一种内存破坏条件,程序在内存被释放后仍继续引用。静态检测这类漏洞具有挑战性。本文探索了利用Binary Ninja的中级中间语言(MLIL)建立数据流图的方法,通过追踪特定内存分配与其他内存区域的交互关系,进行跨函数分析以识别潜在漏洞。
内存分配数据流图构建
数据流图由四种节点类型构成:
- 追踪分配节点(红色):表示待追踪的内存分配
- 函数栈帧节点(绿色):表示函数调用栈帧
- 动态内存节点(蓝色):表示无法确定来源的SSA变量
- 全局内存节点(黑色):表示函数内的全局变量
边表示指针存储操作,包含两个关键属性:
write
:写入位置相对于分配基址的偏移points
:指针值指向的目标偏移
SSA变量与图节点的映射关系
分析从分配函数(如malloc())返回值的SSA变量开始追踪。通过Binary Ninja的get_ssa_var_definition()
和get_ssa_var_uses()
API可以获取变量的定义位置和使用链。指针算术运算时会额外存储偏移信息。
内存加载操作转换为图边
当遇到函数外部内存加载操作时,会假设相关存储操作发生在函数外部,并据此创建图边。内存存储、赋值和加载操作共同构成了数据流图的基础构件。
数据流图信息传播
变量赋值分为三种情况处理:
- 直接变量赋值:简单传递源变量值
- 含指针算术的赋值:更新偏移信息
- 内存加载赋值:通过图边解析目标节点
释放后使用漏洞检测
完成数据流图构建后,分析所有被标记为"Free"的基本块,检查是否存在路径通向"Use"块。同时也会检测从一个"Free"块到另一个"Free"块的路径,以发现潜在的双重释放问题。
分配器/释放器函数的自动识别
通过分析标准函数(如malloc/free)的使用链,可以识别程序特定的包装函数。对于释放器函数,检查函数参数是否直接传递给free()等标准释放函数。
实际漏洞案例分析
工具在多个历史漏洞中成功检测出问题,包括:
- JasPer JPEG-2000中的双重释放(CVE-2015-5221)
- Giflib中的双重释放(CVE-2016-3177)
- GNOME-Nettool中的释放后使用
- OpenSLP中的跨函数双重释放(CVE-2015-5177)
结论与展望
本文介绍的方法虽然存在静态分析固有的分类误差,但为识别释放后使用漏洞提供了有效途径。未来可通过改进日志分组和增加全局内存跟踪来增强工具实用性。项目源代码已公开在uafninja仓库。