漏洞挖掘的艺术-面向源码的静态漏洞挖掘
本文首发于“合天智汇”公众号 作者: 萌新
软件漏洞的挖掘一直是热门的方向,安全从业者们从一开始的手工挖洞,到后来编写自己的工具实现自动化的漏洞挖掘,再到随着近年来AI的蓬勃发展,开始使用深度学习等技术辅助漏洞挖掘,乃至更进一步使用相关技术实现自动化攻防,如DARPA的CGC大赛等。技术日新月异,让人眼花缭乱,本系列文章希望通过介绍典型的代表性工作(侧重于安全学术界的Big4,即四大顶会上发表的成果),来帮助各位师傅们厘清脉络和相关技术,为之后的学术发展或职业发展给出指引,能找到可以深入挖掘发展的方向。
1
按照传统的分类方法,可以分为静态和动态漏洞挖掘技术。
静态漏洞挖掘技术指在不运行目标程序的前提下对目标程序进行分析,这里又可以分为针对源码以及针对二进制程序进行分析其词法、语法、语义等,并通过相关工具获得其AST, CFG, DDG, PDG, CPG等进行辅助分析,来挖掘漏洞。
- 1.1
AST即抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
当初学习C语言写过的辗转相除法的代码为:
while b ≠ 0
if a > b
a := a − b
else
b := b − a
return a
对应的抽象语法树为:
- 1.2
CFG即控制流图(Control flow graph),是一个过程或程序的抽象表现,是用在编译器中的一个抽象数据结构,由编译器在内部维护,代表了一个程序执行过程中会遍历到的所有路径。它用图的形式表示一个过程内所有基本块执行的可能流向, 也能反映一个过程的实时执行过程。下图是一个包含了if和while语句的CFG
提到CFG,很多人都会想到CFI(Control Flow Integrity)控制流完整性技术,这是为了抵御控制流劫持攻击而提出的。我们知道早期的攻击会采用代码注入的方式,通过部署一段shellcode,然后将控制转向这段代码执行,为了组织这类攻击,开发了DEP(Data Execution Prevention)机制来限制内存页不能同时具备写和执行权限。攻击者为了突破DEP,来发明了基于代码重用攻击的技术,利用被攻击程序中的代码片段进行拼接形成攻击逻辑,此类技术包括Ret2libc,ROP,JOP等等,并且被证明为图灵完备的。在可计算性理论里,如果一系列操作数据的规则(如指令集、编程语言、细胞自动机)可以用来模拟任何图灵机,那么它是图灵完备的。换句话说,原先的部署shellcode在跳转执行的技术可以做的一切攻击行为,ROP都能做到。
- 1.3
DDG即数据依赖图(Data flow dependency graph)以最简单的形式表示各个指令之间的数据依赖关系。这样的图中的每个节点代表一条指令,并称为“原子”节点。也可以将它们之间具有简单的def-use依赖关系的某些原子节点组合为包含多指令的较大节点。
以这段为例:
for (int i = 1; i < n; i++) {
b[i] = c[i] + b[i-1];
}
代码中是一个循环体,通过多个def-use依赖关系和内存访问依赖关系构建出的DDG如下
- 1.4
PDG,即程序依赖图(Program Dependence Graph),是程序的一种图形表示,它是带有标记的有向多重图。系统程序依赖图是软件程序间控制依赖关系和数据依赖关系的图形表示。
- 1.5
CPG代码属性图(Code Property Graph)是由ShiftLeft先提出来的,如下所示
CPG为每个应用唯一的代码版本提供可扩展的和多层的逻辑表示,包括控制流图、调用图、程序依赖图、目录结构等。CPG创建了代码的多层三维表示,具有很强的洞察力,这使得开发人员可充分了解应用程序每个版本执行的内容及可能带来的风险。
2
先来看看静态分析方面针对源码的研究。针对源码的漏洞挖掘主要分为两类,分别基于中间表示和基于逻辑推理。
- 2.1
基于中间表示的分析技术主要包括数据流分析、控制流分析、污点分析、符号执行等。事实上,这些技术手段往往会同时应用在研究工作中。
- 2.1.1
数据流分析是一项编译时使用的技术,它能从中收集程序的语义信息,并通过代数的方法在编译时确定变量的定义和使用。通过数据流分析,可以不必实际运行程序就能够发现程序运行时的行为。简单地说,数据流分析就是对程序中数据的使用、定义及其之间的依赖关系等各方面的信息进行收集的过程,以点(5)进行数据流分析如下
- 2.1.2
控制流分析的目标是得到程序的一个控制流图(control flow graph)。控制流图是对程序执行时可能经过的所有路径的图形化表示。通过根据不同语句之间的关系,特别是考虑由“条件转移”、“循环”等引入的分支关系,对过程内的一些语句进行合并,可以得到程序结构。控制流图是一个有向图:图中的结点对应于程序中经过合并的基本语句块,图中的边对应于可能的分支方向,例如条件转移、循环等等,这些都是分析程序行为的重要信息。
- 2.1.3
污点分析是一种跟踪并分析污点信息在程序中流动的技术,其分析对象是污点信息流。污点指的是受到污染的信息。在程序分析中,将来自程序之外并且进入程序的信息当做污点信息,。根据分析的需要,程序内部使用的数据也可作为污点信息,并分析其对应的信息的流向。根据污点分析时是否运行程序,可以将其分为静态污点分析和动态污点分析。
污点分析的过程包括:识别污点信息在程序中的产生点并对污点信息进行标记;利用特定的规则跟踪分析污点信息在程序中的传播过程;在一些关键的程序点检查关键的操作是否会受到污点信息的影响。污点信息的产生点称为source点,污点信息的检查点称为sink点。
以下图为例来说明污点分析过程
将scanf所在的程序点作为source点,将通过scanf接收的用户输入数据标记为污点信息,并且认为存放它的变量x是被污染的。如果在污点传播规则中规定“如果二元操作的操作数是污染的,那么二元操作的结果也是污染的”,则对于y=x+k,由于x是污染的,因此y也被认为是污染的。一个被污染的变量如果被赋值为一个常数,它将被认为是未污染的。对于x=0,将x从污染状态转变为未污染。对于while(i<y),这句所在的程序点在这里被认为是一个sink点,如果污点分析规则规定“循环的次数不能受程序输入的控制”,那么在这里就需要检查变量y是否是被污染的。
- 2.1.4
符号执行是一种用符号值代替数字值执行程序的技术,符号是表示一个取值集合的记号。使用符号执行分析程序时,对于某个表示程序输入的变量,通常使用一个符号表示它的取值,这个符号可以表示程序在此处接收的所有可能的输入。此外,在符号执行的分析过程中那些不易或者无法确定取值的变量也常常使用符号表示的方式进行分析。
符号执行的分析过程大致如下:首先将程序中的一些需要关注但是又不能直接确定其取值的变量用符号表示其取值,然后通过逐步分析程序可能的执行流程,将程序中变量的取值表示为符号和常量的计算表达式。程序的正常执行和符号执行的主要去呗是:正常执行时程序中的变量可以看做被赋予了具体的值,而符号执行时,变量的值既可以是具体的值也可以是符号和常量的运算表达式。
以下图的符号执行源代码为例,函数中的参数x,y分别用符号a,b表示
基于上图的代码可以得到下图所示的程序流程图
可以看到共有三条执行路径,每条路径都对应着一个路径约束(path constrain,PC)。其中返回true的路径有一条,带入符号后,对应的路径约束为a>60&(b*2)==128;返回false的路径有两条,对应的路径约束为a<=60|(a>60&(b*2)!=128)
这个例子表明,使用符号执行技术分析程序,对于分析过程中遇到的程序中带有条件的控制转移语句(条件分支语句、循环语句等),可以利用变量的符号表达式将控制转移语句中的条件转化为对符号取值的约束,通过分析约束是否可以满足,判断程序的哪条路径是可行的。这一部分是符号执行分析的关键部分。由此将判断路径条件是否可满足的问题转化为判断符号取值的约束是否可满足的问题。而对于约束是否可满足的判断,通常使用约束求解的方法,该过程由约束求解器完成(约束求解器是对特定形式的约束表示进行求解的工具)。在符号执行的分析过程中,常使用可满足性模理论(satisfiabilti modulo therries,SMT)求解器对约束进行求解,为此需要将符号取值约束的求解问题转为SMT问题,即一阶逻辑的可满足性判断问题。
- 2.2
基于逻辑推理的漏洞检测方法将源代码进行形式化描述,然后利用数学推理、证明等方法
验证形式化描述的一些性质,从而判断程序是否含有某种类型的漏洞。基于逻辑推理的漏洞检测方法由于以数学推理为基础,因此分析严格,结果可靠。但对于较大规模的程序,将代码进行形式化表示本身是一件非常困难的事情。所以实际上研究价值相对来说并不大。
3
相关工作
这里分别介绍针对源码的静态漏洞挖掘技术部分代表性工作。
- 3.1基于中间表示
发表在2018 NDSS(信息安全四大顶会之一)的K-Miner利用内核代码中高度标准化的接口实现了可扩展性良好的指针分析以及全局的上下文敏感的分析技术,支持对空指针引用、指针释放后重引用(use-after-free, UAF)、指针重释放(double free)、双重检查锁定(double-checked lock)等内存崩溃漏洞的检测。论文及论文作者在会议上的视频见文后给出的参考链接
其实现如图
具体来说,它包括四个分析阶段:在步骤1中,LLVM-IR作为vmlinux bitcode映像传递到K-Miner,以开始进行预分析,这将初始化并填充全局内核上下文。在步骤2中,此上下文信息用于分析单个系统调用,这可以连续多次运行,比如可以分析dangling pointer,use-after-free和double free。在步骤3中,通过各种验证技术对错误报告进行了清理,以降低误报率。在第4步中,使用我们的漏洞报告引擎呈现已排序的报告。
在实验中,K-Miner发现了29处可能的漏洞,总共生成了539个alert,如下所示
- 3.2基于逻辑推理
近年来个人没有看到有相关的工作,不过为了文章的完整性,这里介绍CCS 2002(CCS同样是四大顶会之一)的工作-MOPS。
论文中作者首先确定安全编程实践的规则,将其编码为安全属性,并验证是否遵守这些属性。由于手动验证过于繁琐,因此建立了程序分析工具来自动完成此过程。程序将要验证的程序分析建模为下推式自动机,将安全属性表示为有限状态自动机,并使用模型检查技术来确定在程序中是否可以达到违反预期安全目标的任何状态。
全文逻辑缜密,单单几张图说不清楚,建议有兴趣的师傅们自己去看看这篇论文,下面给出一个简单的例子,以进程权限系统调用为例。
进程权限模型如下
存在风险的系统调用模型如下
描述该属性的复合模型,即在特权状态下,进程不应进行有风险的系统调用。
当然,上面是非常简化的版本,建模越精细,效果自然越好。将linux 2.4.17中的进程权限模型建模如下(包括所有的rued,euid,suid等)
建模之后,文中以一个实例进行了说明
在wu-ftpdversion 2.4中找到了一个已知的安全漏洞。漏洞除了分别在信号SIGPIPE和SIGURG的处理程序中调用seteuid(0)和longjmp(env),基本类似于下图代码中的漏洞
通过在信号SIGPIPE之后立即将信号SIGURG发送到awu-ftpd进程,攻击者可以使该进程调用信号SIGPIPE的处理程序中的seteuid(0)获得特权,然后调用信号SIGURG的处理程序中的longjmp(env)返回到函数main中的setjmp(env)的调用处。 此后,wu-ftpd将使用root特权执行,从而导致攻击者具有root特权。
MOPS能够在wu-ftpd2.4 beta 11中成功检测到该漏洞。
4
参考
1.https://juejin.im/post/5cc51b096fb9a03218555972
2.http://www.jos.org.cn/html/2017/10/5317.htm
3.https://llvm.org/docs/DependenceGraphs/index.html
4.https://zhuanlan.zhihu.com/p/94611033
5.https://cs.nju.edu.cn/chenlin/pages/sat/dataflow.pdf
6.http://www.jsjkx.com/CN/article/openArticlePDF.jsp?id=10342
7.http://staff.ustc.edu.cn/~yuzhang/compiler/2017f/lectures/optimize-6in1.pdf
8.https://www.52pojie.cn/thread-861992-1-1.html
9.https://www.youtube.com/watch?v=1uLKA7Ux8hg
10.https://www.ndss-symposium.org/wp-content/uploads/2018/02/ndss2018_05A-1_Gens_paper.pdf
11.https://www.danielesgandurra.com/research/blast2008-slides.pdf
12.https://people.eecs.berkeley.edu/~daw/mops/
13.http://people.eecs.berkeley.edu/~daw/papers/mops-ccs02.pdf
5
实验推荐
docker搭建vulhub靶场进行nginx服务漏洞分析
Docker是近两年来十分流行的开源容器引擎,Vulhub是一个面向大众的开源漏洞靶场。通过本实验了解Docker的使用,掌握使用Vulhub靶场环境进行漏洞复现。