👍
目前静态分析和动态分析都具有各自的优点和缺点,但是在各自不擅长的领域几乎没有作用。
Q1: 即使结合了动态执行和静态分析,又能怎么用来挖漏洞呢?
作者总结了几种漏洞的属性,静态快速扫描特征,然后指定source点和sink点调用符号执行进行测试,如果使用符号执行验证目标路径可达则确定为漏洞。
首先定义了三种漏洞属性,为静态分析和动态分析提供结合的接口
- 数据流敏感漏洞。对数据流敏感的漏洞可以通过推理source点和sink点之间的数据流来发现。请注意,数据流敏感漏洞的范围严格大于污染型漏洞,污染型漏洞仅包括因未对受污染的用户输入进行清理而导致的漏洞。这些可以通过静态技术实现,具有典型的精度限制,但也允许对数据流进行额外的动态验证以提高精度。
- 易于识别的source或者sink。在漏洞发现过程中,source点决定数据流跟踪的开始,sink点决定数据流跟踪的终止。识别许多漏洞类别的source和sink需要整个二进制文件的精确别名信息,众所周知,这是极其昂贵且不可扩展的。例如,如果source被定义为“从文件 /tmp/secret 读取的任何数据”,则识别所有输入源将需要正确分析打开文件的每个函数和系统调用的参数,以及文件指针、文件描述符的传播,以及相关的数据结构。相反,有些sources或sinks,可以通过检查计算成本低且可扩展的分析生成的工件来识别(例如CFG)。这种sink的一个例子是“库函数 malloc() 的所有调用点”。通过这种类型的source和sink,将程序从source到sink切片是一种经过充分研究的静态技术。尽管通常在静态分析的背景下考虑此属性,但这些切片可以通过动态技术进行处理。这允许动态技术(例如 DSE)对切片过程中的过度近似进行推理,修复此类过度近似,并提高整体系统精度。
- 控制流决定的别名。跟踪数据流几乎总是涉及恢复别名信息,这需要对目标进行昂贵的全二进制分析。然而,我们观察到,许多漏洞合并的数据流根本不涉及指针解引用,或者当它们这样做时,指针总是可以被解析为由控制流确定的一个对象(例如,通过堆栈指针访问的局部变量)。我们认为这种别名的情况是由控制流决定的,这是我们的目标漏洞的先决条件。从静态方面来看,这个属性允许ARBITER自适应地在CFG上前进或后退,而不必担心由不正确的别名信息引起的不精确。从动态方面来看,它极大地简化了由于缺乏动态初始化而导致的别名问题。
Q2: 单纯这三个属性怎么对接静态分析和动态分析?
- 数据流敏感漏洞
- 静态分析: 静态分析可以识别程序中的数据流路径,从潜在的输入源(如用户输入)到潜在的漏洞汇(如不安全的函数调用)。静态分析工具可以构建数据依赖图(DDG),并分析数据如何在程序中流动。
- 动态分析:动态分析,特别是动态符号执行(DSE),可以用来验证静态分析发现的数据流路径是否在实际执行中确实存在。DSE可以模拟程序的执行,检查数据流是否满足特定的漏洞条件
- 易于识别的source或者sink
- 静态分析:静态分析可以利用程序的控制流图(CFG)来识别源和汇。例如,可以识别所有调用特定函数(如
malloc
)的点作为源,或者识别所有可能受到用户输入影响的函数作为汇。- 动态分析:在动态分析中,可以针对这些易于识别的源和汇执行测试用例,以观察数据如何在程序执行过程中流动。这有助于验证静态分析的结果,并发现可能的漏洞触发点。
- 控制流决定的别名
- 静态分析:静态分析中,可以利用控制流信息来限制别名分析的范围,因为许多漏洞涉及的数据流不涉及复杂的指针操作。这允许静态分析在不完全解决别名问题的情况下,仍然能够准确地识别潜在的漏洞。
- 动态分析:在动态分析中,可以利用控制流信息来简化别名问题。例如,如果一个指针总是指向由控制流确定的特定内存区域,那么在动态执行时可以为这个指针分配一个隔离的内存区域,从而避免复杂的别名问题。
Q3: 作者提出了一个叫做Vulnerability Description(VD)的概念,具体这个东西是怎么组成的?
VD其实是由漏洞的特征来编写的,简单来说其包含指定的sink点,source点和满足何种条件时确定为漏洞
Q4: 其中的source和sink点是怎么确定的?
由VD中的sink和source指定的
Q5: 哪些工作体现了静态分析,哪些工作体现了动态分析
首先是sink和source的识别,使用静态分析快速识别sink和source点,然后调用动态符号执行验证是否有可控数据从source点到达sink点
Q6: 如何实现sink点和source点的静态跟踪? 针对作者说的不使用源代码仅使用二进制程序?
作者提出了一种静态数据流跟踪的方法
SymDFT
。给定任何起点(通常是函数的开始),SymDFT以上下文敏感和路径敏感的方式静态地模拟基本块,并对寄存器、内存(作为覆盖全局区域、堆栈和堆的平面内存模型)、系统调用、以及对文件描述符的访问(这捕获了对文件和网络套接字的访问)。
- 遍历策略。在函数级别上,SymDFT按拓扑顺序遍历函数内部的基本块,从而确保始终在访问所有前一个块之后访问一个块。一旦遇到对被调用方的调用,SymDFT将分析被调用方函数,并使用返回的抽象状态在返回站点继续分析。‘
- 分支策略。在每个分支点,它分叉抽象状态并遵循所有分支,无论分支可行性如何:随着仿真的进行,SymDFT收集符号路径预测(即符号约束),但它不评估这些约束的可满足性。
- 状态合并策略。在同一二进制地址的抽象状态被立即合并,其中每个状态被分配一个合并标签,具体和符号表达式被合并成一个if-then-else表达式的形式,合并标签作为条件。
- 终止策略。为了确保分析终止,SymDFT采用了激进的终止策略:(1)当调用深度超过2个函数时,SymDFT不分析被调用方,而是伪造一个抽象的返回值;(2)每个循环最多访问3次。这种激进的终止策略以降低可靠性和精确性为代价换取了可伸缩性。
而后根据CFG在其中识别出source点和sink点并且根据此生成数据流图,在数据流图上对source和sink的追踪很简单就是直接寻找子图即可。
Q7: 作者一直提到欠约束的符号执行(USCE),这个到底是欠缺哪一部分的约束?
这里提到的欠约束的符号执行,实际上是一种对符号执行条件要求的弱化,因为如果通过符号执行的方法得到一条从输入到sink点的路径代价是巨大的,所以如果我们选择从中间某个函数开始进行模拟,可以减少一部分的代价,当然这里减少代价是一部分,并且由于前面的静态分析获得的信息不完整的原因而选择欠约束的符号执行,其可以针对某个函数开始符号执行,其将所有未知值(即条件不足无法推断其值的参数)视作欠约束的变量,针对欠约束变量在源码中不同的使用方法给予不同的约束。
Q8: 别名问题是什么东西
其实就是两个指针指向同一个地址,这两个指针就称为别名,在汇编层面对于这个别名问题的辨别存在巨大困难。
作者提到其认为的最大贡献是弥补的静态分析和动态分析之间的空缺,其将静态分析和动态分析链接起来,使用静态分析来指引动态分析的过程,其中的感觉这个VD和漏洞模板是可以替代Checker的功能的。使用特定的漏洞模板来指引Fuzz过程是否能做到?
很显然,程序只能解决作者提到的PC漏洞,即
例如未初始化漏洞就无法通过Arbiter检测
bash$ git clone https://github.com/jkrshnmenon/arbiter.git
$ python setup.py build && python setup.py install
$ mkdir test
$ ./vuln_templates/run_arbiter.py -f examples/cve-vuln_templates/vd_cve-2018-10388.py -t examples/cve-binaries/cve-2018-10388 -l test/log/ -j test/json/
本文作者:Du4t
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!