题目:Dynamic Test Generation To Find Integer Bugs in x86 Binary Linux Programs
作者:David Molnar, Xue Cong Li, David A. Wagner
单位:UC Berkeley
问题:
整型bug:包括整型溢出、宽度转换、有符号/无符号转换错误
是很多严重安全脆弱性的常见根源 最近(论文2009年的)成为第二大常见bug类型
传统的静态和动态分析技术并不适合解决
之前的方法关注于静态分析或者运行时检查,但会造成大量的误报,因为无法充分准取静态计算整型的值
向应用中插入运行时检查,检查溢出或者没有保持值不变的宽度转换,并在发生时产生异常,这样方法的问题是很多溢出是无害的,这种情况下抛异常阻止了程序运行且导致误报。且有时代码就要依赖于溢出语义,如加密代码或快速哈希函数。
消除误报是很重要的,静态分析中的误报浪费程序员的时间,运行时检查中的误报浪费终端用户的时间。而动态测试生成中的误报浪费工具的时间。由于CPU时间比人力时间便宜,所以动态测试生成是很吸引人的方法。
方案:
提出新方法用动态测试生成揭露x86二进制中的整型bug,介绍了在这样程序中的高效的符号执行设计上的选择
主要方法是利用符号执行构建触发算数溢出、非保留值的宽度转换或者危险的有符号/无符号转换的测试用例,然后在程序上运行这些测试用例并用标准工具检查错误行为。只报告已验证触发了错误行为的测试用例,所以报告出的所有用例都是真实的bug,没有误报。
贡献:
1.设计新方法用符号执行找有符号/无符号转换脆弱性。开发了新类型接口方法,允许我们检查x86二进制trace中哪些值是有符号整型、无符号整形或两者皆是。讨论扩展该分析到Linux媒体播放器软件的挑战
2.扩展符号执行可以发现的整型bug的范围,包括整型上溢、整型下溢、宽度转换和有符号/无符号转换等。之前没有符号执行能包含所有这些整型脆弱性检查的能力。
3.在SmartFuzz中实现这些方法,该工具是Linux的x86二进制上的符号执行和动态测试生成工具。描述了Linux产品软件上进行符号执行的挑战,并解释这些挑战所启发的一些设计上的选择
4.报告SmarFuzz(黑盒)找bug的性能,和黑盒模糊测试工具zzuf(白盒)进行比较。zzuf是简单但有效的模糊测试工具,随机变异给定的种子文件寻找新的测试输入,不需要任何知识或反馈。两者互相找到了对方找不到的bug说明更综合的方法是黑盒白盒都用。
5.发现了大规模报告bug的挑战,并引入了多种技术解决这些挑战。比如为了避免冗余bug报告,开发了fuzzy stack hash解决这些问题。
2008年6月到2008年11月,Metafuzz处理了SmartFuzz和zzuf的超过2,614次测试执行,包括2,361,595个测试用例。这是目前动态测试生成技术报告出的最大的测试运行数和测试用例数。
原理:
1.整型bug源于机器算术和数学算术的失配,比如卷绕问题、宽度转换(从一类机器整型转换为另一种导致非期望的值的改变)、有符号/无符号转换(一个值既被看作有符号的又被看作无符号的)
2.动态测试生成是Godefroid和Engler提出的技术,用符号执行生成揭露特定程序行为的测试用例。
3.想找的三个主要整型bug类型:
1)整型上溢/下溢:当算数表达式大于/小于机器类型能表示的值时发生。常见行为是卷绕。
2)宽度转换:将一个整数类型的值转换为更宽或更窄的有不同值域的其他整数类型。
3)有符号/无符号转换错误:有符号转为同宽度的无符号会引入bug,因为负数会变为很大的整数,反之亦然。
4.动态测试生成:SmartFuzz架构
1)向pool中加一个或多个测试用例,每个测试用例有个分数,根据运行该测试用例时发现的新基本块数目给的分。
2)测试生成的每次迭代,选择分数高的测试用例,作为输入执行程序,用符号执行生成约束,记录每个中间值在程序中被计算时怎样与输入相关。SmartFuzz实现符号执行和给分是用Valgrind二进制分析框架的,用STP求解器求解约束。每个符号化分支处,SmartFuzz添加一个约束,强制程序执行不同路径,然后查询约束求解器看看是否有解,如果有则该结果就是新的测试用例–这种查询称为覆盖率查询(coverage queries)。
如果一个导致错误或潜在错误的条件被满足,SmartFuzz会插入满足的条件,查询结果是导致错误的测试用例–这种查询称为找bug查询(bug-seeking queries)。根据查找的特定的错误,找bug查询分为不同类型。
覆盖查询和找bug查询都以generational search探索(和SAGE类似),每个符号化执行都会导致很多测试用例。
3)triage:判断每个测试用例是否展示出bug,如果是则报告bug,否则把该测试用例加入到pool中评分,可能进行符号执行。triage用Valgrind的memcheck观察具体执行中的常见程序错误。记录每个导致程序崩溃或触发memcheck警告的测试用例。
memcheck检查多种属性,包括读/写非法内存位置、内存泄漏、未初始化值的使用等,低误报率。6.设计选择:
中间表示:用Valgrind二进制插桩工具将x86指令翻译为VEX(Valgrind中间表示)
用中间表示的好处:一定程度的平台独立:当前工具虽然只支持x86,但VEX库还支持AMD64和PowerPC指令集
VEX库产生满足SSA属性的IR,并进行其他优化,使得IR到公式的翻译更直接
可以不用处理x86指令的细节问题 VEX的缺点在于:单个x86指令会扩展为5条以上的IR指令,导致trace长且相关符号化公式也长
在线约束生成:SmartFuzz采用在线(online)约束生成,在程序执行过程中生成约束
相反,SAGE用的是offline约束生成,程序先被追踪,然后replay这个trace以生成约束。
offline约束生成的优势:
在系统调用中对并发或非确定性不敏感,trace比约束生成有更小的运行时开销,所以可以在现实环境正在运行的系统中应用
分开比较好开发和调试
SmartFuzz采用在线约束生成,因为之前不知道有可用的有类似VEX中间表示的线下trace-replay框架,现在O’Callahan的chronicle-recorder可以为一个基于VEX的线下约束生成工具提供起点。
内存模型: EXE和KLEE等符号执行工具以符号化数组集合形式建模,每个数组表示被分配的内存对象
本文工作是对于每个load或store指令,首先在访问符号化堆之前具体化内存大小,维护一个映射M,从具体内存地址到符号化值映射。如果程序从具体地址a读取,就去M(a)读取符号化值。即使记录了与该地址关联的符号化表达式也忽略该符号化地址。符号化a的值在约束生成阶段就知道了,所以是个常量。
这种处理会损失精度,但可以对大型trace有好的扩展性。具体化地址使得生成求解器可求解的符号化公式更高效,因为求解器不需要知道指针别名关系。
只有被污染的数据是符号化的:追踪内存中每个字节的污染状态,没有被污染的内存说明不依赖于不受信任的输入,不必维护符号化信息,这样可大大减少约束系统的大小,减少符号执行的内存开销
关注Fuzzing文件:关注于单线程程序,从文件中读取不可信的数据,测试用例即是文件的内容,SmartFuzz关注于生成候选文件,这简化了符号执行和测试用例生成基础结构,因为读文件的系统调用个数有限,也不需要考虑同一程序多个线程间的并发交互。
多合作分析:利用Valgrind插桩架构中一系列独立的合作的分析,每个分析向基本块中添加自己的插桩,并向其他分析提供接口。 可扩展性好,添加新特性就添加一个新分析即可,然后修改核心约束生成器。
这个分解使得我们能够提取污染追踪代码,很小的修改就可以把它用到不同工程中
后处理的优化:输出的约束尽可能接近中间表示,只快速地进行有限的优化。
如采用相关约束消除作为后处理步骤,然后留给约束求解器执行常见的子表达式消除、常量传播以及其他的优化。
好处:简化约束生成
弊端:
当前的约束求解器还不能记住从一个查询到下一个的优化,导致求解器中有冗余工作。
虽然优化后每个查询是小的,但一个程序包含所有查询的整个符号化trace可能会有吉字节。
7.找整型bug的技术:
上溢/下溢:每个算数表达式都可能上溢或下溢,添加一个上溢或下溢发生时满足的约束,如果求解器能满足这些约束,则得到的输入值可能导致下溢或上溢,潜在地导致非期待的行为
宽度转换:添加约束检查源值是否可能超出目标值的范围。宽度转换可能会符号扩充,用约束求解器搜索一个源值高比特位为非0的测试用例
有符号/无符号转换:基本方法是从x86指令执行中重构所有整数值的有符号/无符号类型信息。
四种类型整型:Top:还没有在有符号或无符号整型的上下文中被观察
Signed:该值被用作有符号整型
Unsigned:该值被用作一个无符号整型
Bottom:该值被前后不一致地既用作有符号又用作无符号整型
想找的是“Bottom”类型的符号化程序值,然后尝试合成一个输入使这些值变为负数。
给Valgrind中间表示的每个临时变量的实例关联一个类型,开始时是Top,执行过程中为每个值的类添加类型约束(type constraints),x86二进制中类型约束的来源有有符号和无符号的比较操作、memcpy等函数的长度参数
记录类型信息:用一个union-find数据结构,把整型值分为等价类,每个等价类维护一个类型即可,赋值使得源值和目的值有同样类型,把它们的等价类合并。更新一个值的类型可以通过更新它的代表节点类型,无需更新等价类中其他变量的类型。
VEX IR中所有的值要么存在内存中、要么在寄存器中、要么在临时变量中临时变量的生命周期仅限于所在的单个基本块中,因此维护一个当前基本块临时变量的列表,离开基本块时临时变量相关的类型信息都被删除8.大规模分类和报告:
构建web服务Metafuzz管理大量的测试
Metafuzz架构:测试机器为一个程序产生新测试用例并在本地运行,确定哪些测试用例能展示出bug,把这些用例发送给MetaFuzz。MetaFuzz网页将这些用例展示给用户,带着在哪个目标程序发现的什么类型的bug等信息。
实现:
在原型工具SmartFuzz中实现该方法,用于分析Linux x86二进制可执行程序,不需要源代码。不需要修改程序的build过程(静态分析的痛点)。且可以分析整个程序,可以找到由于与应用和库进行交互而引起的bug,即使没有这些库的源码。
创建一个报告服务metafuzz.com用于分类和报告SmartFuzz和黑盒fuzz测试工具zzuf发现的bug
评估
在mplayer多媒体播放器、exiv2图像元数据库、图像处理程序conver等软件应用上进行实验
用亚马逊弹性计算云中执行测试,metafuzz.com记录了超过2614次测试执行,包含2,361,595个测试用例,发现了约77个不同的bug
局限
实现中将输入文件的每个字节和一个符号化输入变量关联,导致SmartFuzz无法生成大于初始种子文件中的字节数的测试用例
工具: http://www.sf.net/projects/catchconv
安装: http://catchconv.pbworks.com/w/page/15390708/Getting Started
相关工作
1.Godefroid的工作用动态测试生成以及bug-seeking查询找整型上溢、下溢和其他收缩转换的错误,本文考虑的类型比它多。
2.IntScope:找整数bug的静态二进制分析工具,将二进制翻译为中间表示,用符号执行分析流到“被污染的sink”的数据,惰性检查潜在的有害的整型溢出。直接将错误报告给程序员。关注于溢出错误。
3.Lanzi提出x86动态测试生成的设计,利用循环的静态分析辅助求解器,但只是初步实现
4.KLEE工作在LLVM的中间表示上
5.Larson和Austin应用符号化域分析追踪程序检查潜在的缓冲区溢出攻击,但并没有想要合成崩溃输入。
6.Song的BitBlaze框架对x86二进制进行符号执行,但关注于恶意软件和签名生成,而不是测试生成
7.其他检查整型bug的方法包括静态分析和运行时检查,两者都会标记正确的代码(误报),而本文工作只会报告导致崩溃或Valgrind错误报告中的用例
微软Prefast工具用静态分析警告过程间整型溢出,Microsoft Visual C++和gcc都可以添加运行时检查,捕获malloc函数参数中的整型溢出并中止程序
Brumley为运行时检查提出规则,这些规则可以在x86架构上实现,用jumps conditioned on the overflow bit in EFLAGS,可以低开销
以上两种方法都没办法捕获有符号/无符号转换错误
介绍一种动态测试生成方法,通过符号执行技术揭示x86二进制Linux程序中的整型Bug,包括整型溢出、宽度转换等问题。该方法通过构建触发特定行为的测试用例来检测错误,避免误报。
3627

被折叠的 条评论
为什么被折叠?



