聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长

基于定向模糊测试的漏洞验证技术研究

2021-09-06 17:05 浏览: 3368006 次 我要评论(0 条) 字号:

静态检测分析中对程序源代码进行建模,对程序进行抽象,会引入实际不可达路径和不可达状态,因此会存在大量的误报漏洞,人工分析判断静态检测结果是否为误报费时费力,亟需一个可行的解决方案对静态检测结果进行漏洞验证,省去人工对静态检测结果的繁杂分析。如果能够找到一个输入可以触发静态检测报告的可疑漏洞,就可以排除误报的可能。定向模糊测试技术有强大的路径探索能力,能够在给定的位置找到触发程序崩溃的用例,符号执行能全面系统的访问被测程序的内部状态。我们可以将静态工具报告的漏洞位置作为定向的目标,利用定向模糊测试与符号执行技术,试图找到一些在目标行位置崩溃的输入。

背景与动机
针对C/C++源代码程序,越来越多的研究将深度学习应用到漏洞静态检测。但是由于程序建模的不完善,漏洞规则定义的疏漏,深度学习模型的不足,这些静态检测得到的漏洞有可能是误报,实际上并不能触发。一个有效的解决方案是使用定向模糊测试尝试找到触发该漏洞的输入,从而验证该漏洞是否真实存在。但是目前的定向模糊测试效率低下,需要优化定向模糊测试技术从而实现漏洞验证这个功能。目前比较成熟的定向模糊测试方案是AFLGo,但是它存在一些缺陷限制定向的速度。第一,AFLGo将新加入的种子放到队列尾部,按照顺序从队列中拿取种子,只是单纯根据距离分配不同的能量于不同种子。这样不能优先变异短距离的种子,会影响定向的效率。第二,AFLGo对所有种子都采取随机变异方法。这种变异手段在路径探索方面及其有效果,可以生成大量不同的执行路径种子,如果对接近甚至到达目标位置的种子执行这种变异方法,会导致生成的测试用例偏离目标位置,限制AFLGo队列中的种子到达目标位置的速度。因此,本文改进定向模糊测试AFLGo并且引入符号执行帮助其探索路径,找到触发可疑漏洞的输入,从而实现漏洞验证功能。

该成果已在实验室github组织下开源,请访问https://github.com/CGCL-codes/SCVDT/tree/main/HDVerify获取相关内容。


设计与实现
图1展示漏洞验证方案的概览。漏洞验证主要分成两个阶段:1)面向目标位置崩溃生成,输入是待检测的C/C++软件源代码,编译脚本,可疑漏洞位置行,输出是所有找到的崩溃用例;2)崩溃匹配阶段,输入是所有的崩溃用例,输出是漏洞验证报告。
                                         

图1  漏洞验证概览图
 
(1)面向目标位置崩溃生成阶段
首先我们对程序进行静态分析,该过程提取出程序的函数调用图(CG)和每一个函数的控制流图(CFG),并且找到可疑漏洞位置所在的基本块和函数,继而确定每一个函数到目标函数的距离和每一个基本块到目标基本块的距离。然后将获得的基本块距离信息插桩到程序中,编译出一个可执行的插桩二进制程序,这个基本块距离信息在种子距离计算中将被使用到,同时编译出一个没有插桩的二进制可执行程序。接着启动定向模糊测试与符号执行的流程,负责生成触发可疑漏洞的崩溃用例,使用符号执行生成符合该程序输入要求的初始种子,如果符号执行不能生成初始种子,将收集到的符合程序输入格式的文件作为初始种子。定向模糊测试的过程中,如果在某一个时间阈值内没有距离更短的种子生成,则从定向模糊测试的种子池中选择一个距离最短的种子交给符号执行引擎,当符号执行引擎通过反转分支条件生成有新的测试用例后,定向模糊测试器将这些测试用例作为新的种子,按照距离对这些测试用例进行排序,插入到定向模糊测试的种子队列中。
 

图2  定向模糊测试方法概览图
 
面向目标位置崩溃生成阶段中最核心的部分是定向模糊测试,其主要过程如图2所示。
1、首先读取初始种子到种子池,这个初始种子来源于两个地方,一个是符号执行求解的符合程序输入格式要求的测试用例,另一个是程序提供的合法格式的输入;
2、接着状态调度模块确定当前定向模糊测试的运行状态,主要涉及到探索(exploration)阶段和挖掘(exploitation)阶段的切换,这两个状态对在种子选择和种子变异上会采取不同的策略。如果当前状态是exploration,从种子池中选择执行速度快,执行路径长的种子;如果当前状态是exploitation,从种子池中选择距离最短,执行次数少且发现路径多的种子;
3、然后按照种子的距离确定该种子的能量,能量高的种子拥有更多的变异次数,距离近的种子分配比较多的能量,距离远的种子分配比较少的能量;
4、接着对被选择的种子进行适应性变异,对距离小的种子主要采用细粒度变异,包含比特翻转,比特替换,比特删除和距离指导变异方法,可以防止其生成的测试用例的执行路径偏离目标代码区域,距离大的种子主要采用粗粒度变异,包含HAVOC,SPLICE等变异策略。如果出现崩溃测试用例则将其加入到崩溃集合中,如果变异得到的测试用例没有带来新路径则将其抛弃,否则将该新测试保留下来并作为下一步骤的输入;
5、然后计算变异得到的新测试用例的距离,并且将该测试用例作为新的种子加入到种子池中。以上过程循环往复,在迭代过程中不断获取距离更小的种子,最终会得到执行路径到达或者经过可疑漏洞位置的种子,并且在可疑漏洞位置附近发现崩溃用例。
 
(2)崩溃匹配阶段
在崩溃匹配阶段我们对获得的崩溃测试用例进行分析,找到与可疑漏洞匹配的崩溃。定向模糊测试只负责生成崩溃用例,这些用例可能是在可疑漏洞位置附近,也可能不在可疑漏洞位置附近,并且这些崩溃用例缺少崩溃发生的位置信息和发生崩溃的原因,因此需要进一步确定哪一个崩溃能够和可疑漏洞位置匹配上。
使用ASAN工具复现该崩溃,可以获取如图3所示的运行时栈信息,可以直接提取出崩溃用例的函数调用链,崩溃发生的源代码行和崩溃发生的具体原因。继而我们通过LLVM的API得到崩溃行所在的基本块和可疑漏洞所在的基本块。然后就可以进行崩溃匹配的步骤。
 

图3  ASAN报告示例
 
崩溃匹配的流程见图4,崩溃匹配分成三种粒度的匹配,函数级别的匹配、基本块级别的匹配、源码行级别的匹配。其中源码行级别的匹配要求最严格。首先匹配崩溃发生的行与可疑漏洞行,如果一致就说明该崩溃用例能够触发该可疑漏洞,表示匹配成功,则输出漏洞验证成功报告。对于一些漏洞,程序执行路径到达漏洞位置的时候,程序不会立刻发生崩溃,而是会继续运行在程序的其它位置发生崩溃。因此可以放宽匹配条件,如果崩溃发生所在基本块与待验证可疑漏洞所在基本块一致,也表示匹配成功,认为该崩溃用例能够触发可疑漏洞。如果在基本块粒度上还没有匹配成功,就放宽到函数粒度的匹配,崩溃的运行栈中存在可疑漏洞所在函数,则表示匹配成功,该崩溃用例能够触发可疑漏洞。其它情况下则表示匹配失败。当匹配成功后,将该崩溃用例作为触发可疑漏洞的输入,获得一份漏洞验证报告,包括可疑漏洞位置、触发该漏洞的输入、崩溃发生原因。
 

图4  崩溃匹配流程图
 
最后在真实软件上进行实验评估,将已知漏洞作为验证的目标,分别使用AFLGo、本文所提方法HDVerify和没有使用符号执行组件的本文方法HDVerify-尝试找到触发这些已知漏洞的输入,实验运行结果如图5所示。实验结果表明本文所提漏洞验证方法有效,可以找到触发可疑漏洞的输入。并且本文使用方法完成漏洞验证所用时间更少。
 

图5  漏洞验证时间对比


网友评论已有0条评论, 我也要评论

发表评论

*

* (保密)

Ctrl+Enter 快捷回复