APP下载

基于污点分析与符号执行的Web漏洞检测

2022-12-03刘行波李源林余明俊喻金龙郭运丰孔华锋羌卫中

计算机应用与软件 2022年11期
关键词:污点静态漏洞

刘行波 李源林 余明俊 郑 炎 喻金龙 郭运丰 孔华锋 羌卫中

1(湖北华中电力科技开发有限责任公司 湖北 武汉 430077)2(武汉商学院 湖北 武汉 430056)3(华中科技大学网络空间安全学院 湖北 武汉 430074)

0 引 言

随着互联网的迅猛发展,Web应用越来越多地与人们的生活紧密联系到一起。Web应用是Web服务器与用户交互的接口,Web应用中不可避免地存在各种类型的安全漏洞,这些漏洞给用户和服务提供者带来了很大的安全威胁。Web应用的普及以及漏洞的无处不在,使得针对Web应用的攻击也成为主要的攻击方式。Radware发布的2018年Web应用安全状况指出,至少89%的受访者在过去一年都遭遇过针对Web应用或Web服务器的攻击,近一半(46%)的企业在过去一年都遭受过数据安全泄露事件。

漏洞是各种网络空间安全事件发起的根源,网络安全事件频发的同时,针对代码漏洞的检测方法也应运而生,例如污点分析技术、符号执行技术。污点分析技术是用于恶意代码检测、软件安全漏洞检测的关键技术。污点分析的目的是追踪程序中不确定输入的传播路径,并判断最终是否能引发安全问题。污点分析技术分为静态污点分析和动态污点分析。

动态污点分析指通过插桩的方式,在程序运行时获取程序的数据流和控制流,实时跟踪敏感信息在程序中的传播。动态污点分析只能分析程序运行的代码,分析结果受代码覆盖率影响,因此会产生漏报。此外,动态污点分析工具的性能开销较大,特别是随着代码混淆技术的出现,动态分析的开销进一步加大。

静态污点分析是通过语法与词法分析等方式,静态地分析变量数据与程序间的关系[1],其模型包括source、污点跟踪、sink三部分。Source即污染源,表示程序从外界获取的不可信输入;Sink即污点汇聚点,通常为一组安全敏感函数;污点跟踪通过数据流分析技术跟踪程序中污点数据的传播,判断污点数据能否从source达到sink。代码中的污点数据输出到安全敏感函数中,会造成敏感数据泄露或者非法访问。相较于动态污点分析方式,静态污点分析具有不需要额外的内存空间、分析速度快、分析全面的优点,但存在很多的误报。

动态符号执行技术是一种经典的程序分析技术,其思想是将程序中变量符号化,模拟程序的执行,并收集路径上的约束条件。动态符号执行过程如下:为程序生成一个随机的输入,模拟程序的运行并收集程序执行路径上的约束条件,对约束表达式中的约束取反并求解,将得到的解作为程序新的输入执行,循环这个过程,直至遍历所有路径或者达到其他中止条件。符号执行过程中可以引入漏洞分析规则,分析程序是否满足规则以判断是否存在漏洞。

符号执行面临的问题之一是路径爆炸,方法调用和循环结构会使程序产生大量分支,使分析的复杂度呈指数级增长,直接影响符号执行的进行。其次,路径选择策略会影响符号执行效率,对于覆盖率引导的符号执行工具,需要使用合理的路径选择策略,使得符号执行在短时间内达到尽可能高的覆盖率;而对于定向模糊测试工具,其目标是尽快到达目标代码区域。受限于约束求解器的能力与其巨大的开销,符号执行技术还无法应用于较大的程序。

针对以上问题,提出一种检测代码漏洞的思路,使用静态污点分析技术得到可疑漏洞的位置信息,记录下从程序入口到达可疑漏洞的路径信息;使用符号执行工具动态分析程序,收集路径约束,并对包含可疑漏洞的路径约束表达式求解,排除无解和不可达的可疑漏洞。为了增加静态污点分析的有效性,使用细粒度的污点记录,并对程序的分支进行简单判断。

1 相关工作

动态污点分析工具TaintDroid[2]重新设计了用于存储数据包的对象结构,通过自定义的污点传播规则跟踪污点数据在组件间的传播。TaintDroid采取了比较保守的策略,因此误报率较高。动态污点分析系统DYTAN[3]可以同时对数据流和控制流进行跟踪,使污点分析获得的信息更加全面。

基于静态污点分析技术,研究人员开发了很多漏洞检测工具。FlowDroid[4]是一个针对上下文敏感、流敏感、域敏感和对象敏感的精确污点分析工具。MudFlow[5]是一个对Android应用程序进行漏洞检测的工具,但会出现大量误报的情况。MultiFlow[6]提出了一种分析结果中的多个source是否绑定sink的污点分析技术,利用正常应用存在差异的特点可以降低虚警率。Taj[7]是一个面向Web应用的污点分析工具,它使用执行指针分析技术,构造应用的方法调用图并将方法调用图作为输入,并设计了一种混合的轻量级切片算法跟踪污点信息流,但是它忽略了程序的控制依赖关系。

基于符号执行的白盒模糊测试工具KLEE[8],使用了多种启发式搜索策略,有效缓解了路径爆炸的问题。BitBlaze[9]采用广度优先的路径搜索策略,使用TEMU插桩工具对二进制程序进行符号执行操作。SAGE[10]使用分代搜索算法探索未知路径,以提高生成测试样例的有效性。

符号执行可用于与其他技术结合。例如,符号执行用于定向模糊漏洞测试的Dowser[11]。但是,定向符号的有效性是以效率为代价的[12],需要花费大量时间进行繁重的程序分析和约束求解,开销较大。而且,基于模糊测试的漏洞检测,随机性比较强,代码的覆盖率也比较低,是一个不太稳定的方案。蔡军等[13]结合静态分析与动态符号执行技术,标记程序中的敏感函数,生成可以到达安全敏感函数的测试样例,样例可作为后续模糊测试的种子,但这些样例并不一定会触发漏洞,且静态分析也并未提高符号执行的效率。在符号执行与污点分析结合方面,文献[14]提出使用符号执行与动态污点分析相结合的技术,以提高代码的覆盖率和污点路径的追踪效果,但是对于更加细粒度的分析,会造成巨大的开销,而且十分不稳定。辛伟等[15]提出使用动态污点分析与符号执行相结合的技术,使用动态污点分析技术得到可疑漏洞并记录路径上的条件约束表达式,使用Z3求解器直接对约束表达式求解。

2 系统设计

漏洞检测系统架构如图1所示,主要包括配置模块、代码解析模块、污点分析模块和符号执行验证模块等。其中:配置模块主要用于配置污点源、净化函数、污点汇聚点、漏洞规则库;代码解析模块主要用于构建程序的基本块和控制流图;污点分析模块主要用于追踪污点数据在程序间的传播情况;符号执行模块主要用于分析漏洞触发点的可达性。

图1 系统架构

分析开始之前,需要配置污点源、净化函数、污点汇聚点、漏洞规则库,漏洞规则库中保存待检测漏洞的信息和对应的检测规则。在静态代码分析中,方法的调用会对变量的状态产生影响,所以需要按照程序调用图进行分析,保证在分析函数调用对变量的影响时,被调用函数的行为已经被分析。污点传播分析基于数据流分析,需要对方法字节码进行遍历。

符号执行模块作为漏洞验证模块。静态污点分析得到的可疑漏洞可能存在误报情况,因为在静态分析中,默认所有的代码都会被执行,但实际运行中存在不会执行的路径。符号执行模块主要用于检测可疑漏洞的可达性,只有路径可达的可疑漏洞才会被报告出来。

3 静态污点分析模块

静态污点分析模块用于模拟程序运行时变量状态的变化,跟踪污点数据在程序内的传播。程序的执行过程可以看成程序状态转换的过程,不同的状态对应着不同的栈帧信息和变量信息。每个方法在执行的同时都会创建一个栈帧,用于保存局部变量表、操作数栈、动态链接和方法出口等信息,局部变量表中存放方法参数和方法内部定义的局部变量。操作数栈是Java虚拟机的工作区域,大多数指令操作都需要经过操作数栈:数据入栈,执行运算,返回值入栈。使用大小可变的List模拟栈帧中的局部变量表和操作数栈,记录局部变量和操作数栈中的变量。

图2中字节码为待分析字节码,图3为栈帧状态记录,localVar代表局部变量表,大小固定为3slot,opStack代表操作数栈,大小可变,从索引位置3开始。初始状态下,局部变量表中三个slot处的变量为var0、var1和var2,它们的状态分别为taint、safe和unknow。分析字节码指令,aload指令为对象入栈操作,var0、var1分别入栈,随后调用append函数,因为append的返回值状态会受到栈偏移1和0位置处的变量即var0、var1状态影响,返回值状态为这两个变量中污染程度较高的状态taint。astore指令将操作数栈中的元素保存至局部变量表,局部变量var2状态变为taint,污点信息从var1传播到var2。

图2 数据流分析字节码

图3 局部变量状态变化

为了提升污点分析的准确性,使用细粒度污点表示结构,并通过简单的路径判断排除部分无效路径。

3.1 细粒度污点表示

静态代码分析中,如果将变量作为污点状态记录的最小粒度,可能会导致状态表示不准确。例如,非基本类型对象被感染时,可能是对象的某些字段被感染,如果没有额外的记录,只将对象状态标记为taint,就会扩大污染的范围。collection、map结构也同理,将整个collection或者map看成一个变量,该变量只会存在一个整体的状态taint、unknow或者safe,但实际上容器内部元素的状态是不同的。

图4是可能产生干扰的代码片段,List中保存了安全的字符串变量与污点数据param,通过存取操作,程序将污点数据param赋值给bar,如果改变List的存取操作或者操作的参数,返回值bar的状态也可能改变,检测程序需要分析该操作过程才能准确得到变量状态,否则可能产生误报或漏报。

图4 干扰代码示意

针对由分析粒度粗糙引起的误报问题,需要对变量进行更加详细的状态记录,同时需要对相关操作的字节码、函数进行解析。增加额外记录如图5所示,var0是普通的非基本类型对象,增加了字段状态记录,var1是有序的List,增加了内部变量状态记录,var2是Map,增加了内部key对应的value状态记录。

图5 细粒度状态记录

与记录相匹配的是对相关字节码操作,在字节码层面看来,putfield/getfield/putstatic/getstatic是对字段的操作。字段被更新时,需要对当前操作对象状态进行判断,同时更新变量的额外记录。字段入栈时,需要从额外记录中得到字段变量的状态,并将状态更新至栈顶。如果不存在准确有效的额外字段记录,使用对象自身的状态代替。

对于List对象,增加的额外空间只需记录其对应索引处的对象状态,对于Map对象,增加的额外空间需要记录key对应的对象状态。因此,额外的记录并不会带来太大的开销。在最坏的情况下,如果没有List、Map内部对象的准确状态信息,将这些集合看为一个整体分析对象,使用内部对象最危险状态作为内部全体对象状态。

3.2 路径选择

一般而言,静态污点分析问题都是被转化为数据流分析问题进行求解:首先为每一个过程构建控制流图或者过程间的控制流图,然后根据不同敏感度(上下文敏感、流敏感等)进行具体的数据流传播分析。

在代码分析过程中,基本块是程序执行中最大的连续单位,也是程序控制的最小单位。由于程序并不一定是顺序执行的,程序中可能存在分支判断指令,相对应地,基本块之间会存在分支与合并的情况。基本块的分支代表了程序可能存在的执行路径。在静态分析过程中,很多情况下不能判断分支路径的执行情况,因此,很多漏洞分析工具在进行分析时默认程序会执行所有路径。但是同一个变量在不同路径上的处理是不一样的,合并所有路径会使某些本为安全的变量被误标记为污染状态。因此,在进行污点数据流分析的过程中,加入对分支路径的判断,以避免无效的分支路径对污点数据流分析过程的干扰,可以有效降低误报。

常见的路径分支控制指令分为两类:条件跳转指令和表跳转指令。条件跳转指令的操作数一般是两个目标地址,分别是判断成功或者失败跳转的地址。表跳转指令对应到代码层面就是switch-case结构,表中可能存在多个跳转地址。因此要想得到这些指令的准确跳转地址,需要判断跳转条件是否满足。

因此在污点分析过程中需要维护常量值:如果分析的指令为常量入栈操作,需要在变量记录中保存对应的常量值,例如String、int、boolean等类型数据;如果分析的指令为运算表达式,取出操作数栈对应位置变量的常量值运算,并保存结果;如果分析的指令为字符串拼接、截取操作,取出操作数栈对应位置变量的常量值进行相应的操作,并保存返回值。在此基础上,如果分支条件表达式的结果可以通过操作数栈内元素的常量值计算,那么路径判断成功,否则失败。

由于一个方法只存在一个入口块,在对方法进行分析的过程中,首先初始化入口块,后续所有的基础块状态都是由入口块的状态和方法字节码指令决定。基础块中存在是否会被执行的标记,初始块即入口块为可执行块。存在分支时,如路径判断成功,将跳转目标地址块标记为可执行块,将非目标地址块标记为不执行块。如果当前块的所有前驱块均为不执行块,那当前块也为不执行块,否则为可执行块。不执行块在程序执行时不会执行,不会产生污点传播,也不会访问sink函数,因此可以跳过这些块的检测,以降低无效代码带来的误报。

以图6为例,待分析函数的CFG由四个基本块组成,block0存在分支语句,假设通过对其条件表达式判断可以得到该块的跳转地址为33。对block1进行分析时,可以知道其前驱块存在分支,并且分支目标地址为33,与本块的起始地址不同,block1的执行标记flag被置为false。对block2进行分析时,可以得到其前驱块为block0,block0的跳转地址为33,等于本块的起始地址,本块的执行标记flag为true。block3存在两个前驱块,并且前驱块block2的执行标记为真,block的执行标记也会置为真。对执行状态为false的块的扫描不会对内存记录产生影响,即可以忽略block1中的任何操作,block3的入口状态完全取决于block2,block1中的污点传播行为并不会生效。

图6 路径选择示意图

3.3 污点以及sink的过程间传递分析

污点和sink的过程间传递是污点分析中很重要的一个过程,污点分析过程间传递涉及到状态的参数传递、返回值传递、字段感染。一般来说,函数的调用过程为调用对象入栈、函数参数入栈、函数调用、返回值入栈、返回值出栈。调用的函数可能会对调用对象的字段产生影响。由于静态分析并不执行程序,当sink函数被非安全的元素影响时,需要对该处位置进行记录。这里说的非安全元素是指被函数参数影响到的变量,但是并不确定函数参数是否安全。将这些函数加入到自定义sink列表中,并标注出受感染的参数列表。在后续的扫描中,如果再次扫描到自定义sink被调用,需要检查其受感染参数列表,并与当前参数列表进行合并,如果当前对应位置参数状态还是非安全的不确定状态,需要将当前函数加入到sink列表中,并记录受感染参数列表。循环以上操作直到确定参数是安全或者被感染。

在分析函数的参数、返回值和字段之间的感染时,需要同时分析这三者之间的感染情况。函数的参数可能影响到调用对象的字段,也可能影响到函数的返回值。调用对象的字段可能会影响到函数的返回值。问题在于如何判断它们之间的互相影响,字段的操作可以通过字节码指令识别出来,返回值即为调用return指令时的栈顶元素,入参可以在函数开始处标记。对于每一个函数,在首次进行扫描时记录下这三者之间的关系,其他函数调用该函数时,可以直接利用之前扫描得到的信息进行污点传播分析。

在图7代码中,getParameter()函数为source,prepareStatement()函数为sink。系统会先分析getName方法,方法参数状态为unknow,并最终传播到sink,由于参数不确定为安全状态,系统将getName加入到sink列表,并且该方法的返回值状态会受到参数状态影响。回到dopost方法,id为taint状态,并且最终影响到getName函数,系统会将此调用路径加入可疑漏洞列表。受到过程间污点传递影响,变量name状态也会被感染为taint状态。

图7 污点以及sink传递代码

4 符号验证模块

符号验证模块为系统提供漏洞可达性验证功能,验证污点分析得到的漏洞是否可达,从而排除一些不可达漏洞。路径可达性通过对目标代码位置处的路径约束表达式求解得到,静态分析中,得到可疑漏洞的位置,并记录下可疑漏洞处的约束表达式。在符号执行过程的约束求解步骤中,对比当前约束表达式与可疑漏洞对应的约束表达式,如果表达式一致,对其求解,如果有解,表明可疑漏洞是可达的,否则可疑漏洞不可达。

以图8所示代码为例,函数foo存在两个参数x和y,getSource()是source函数,其返回值s被视为污染状态。sinkA与sinkB是两个不同的sink函数,这两个函数的参数均为被污染的变量s,静态分析得到该程序中存在两个可疑漏洞sinkA()与sinkB(),sinkA()对应的约束条件为{a+b≥0},sinkB()对应的约束条件为{a+b<0}。

图8 漏洞程序代码

使用符号执行工具进行验证,该函数存在两条路径,分别对应if分支判断的两种情况。系统先产生随机输入,例如{x=1,y=2},然后执行程序。第一次执行代码,程序会运行至sinkA(),此时路径约束条件为{x×x+y×y≥0},对比可知此约束表达式为sinkA()对应的路径表达式,证明sinkA()可被触发。随后将约束取反对另一条路径进行求解,路径约束取反得到{x×x+y×y<0},对比可知该路径表达式为sinkB()的约束表达式,该约束表达式无解,因此sinkB()不可达。程序中只有一条有效路径,并且只存在一个漏洞sinkA()。

5 实验分析

实验环境为Windows 10操作系统、8 GB内存。实验对比工具为商业检测工具Fortify的静态代码分析功能Fortify Static Code Analyzer。实验数据集为OWASP Benchmark,OWASP Benchmark是一个免费和开放的测试套件,旨在评估自动化软件漏洞测试工具和服务的速度、范围和准确性,测试使用的版本包含2 740个测试用例,覆盖了Command Injection、SQL Injection、Xpath Injection、XSS、LDAP Injection等数11种类型漏洞。Benchmark中的一个Test Case对应一种类型漏洞,同种类型的漏洞存在对照样例。Benchmark中漏洞类型统计如表1所示。

表1 Benchmark漏洞类型统计

OWASP Benchmark使用Maven工具编译,项目中提供了运行应用的脚本runBenchmark.sh/bat,可以搭建起可供访问的应用。由于此系统和Fortify均为静态分析工具,不需要搭建运行环境,只需要编译项目。

在检测效果评价方面,使用误报率、漏报率和综合评价指标F1进行评价,漏报率=FN/(TP+FN),误报率=FP/(TP+FP),F1=2PR/(P+R)。

Benchmark中漏洞种类较多,因此给出表1部分类型漏洞的测结果对比以及样本总体检测结果对比,表2-表4分别是SQL注入漏洞、XSS漏洞和命令注入漏洞检测结果对比,表5是样本总体检测结果对比。

表2 SQL注入检测对比

表3 XSS检测对比

表4 命令注入检测对比

表5 总体样本检测对比

可以看出,在OWASP Benchmark数据集上,本文设计的漏洞检测系统性能明显优于Fortify,误报率和漏报均低于Fortify,综合指标高于Fortify。

为了说明路径判断、细粒度污点分析等优化机制对检测结果的影响,去掉优化措施后再次对数据集进行检测。表6展示了有无路径判断、细粒度分析等优化机制的总体样本检测结果,无优化的系统误报率急剧上升,漏报率差别不大。结果表明路径判断机制和细粒度分析机制可以大大提高系统的抗干扰能力,提升分析的准确性,提升检测效果的有效性。

表6 有无优化机制下总体检测对比

6 结 语

本文设计实现基于静态污点分析的漏洞检测工具,使用细粒度的污点记录,并对程序的执行路径进行判断,对污点的传播过程进行精确的分析,提高了静态污点分析的有效性。最后使用符号执行的方法对漏洞的可达性进行了判断,很好地克服了静态分析的不足。实验结果表明,该系统可以有效地检测常见的Web漏洞。有待进一步研究的地方有:外部api调用带来的不确定性;符号执行验证模块效率过低的问题。

猜你喜欢

污点静态漏洞
漏洞
基于代码重写的动态污点分析
最新进展!中老铁路开始静态验收
静态随机存储器在轨自检算法
污点
使用Lightroom污点去除工具清理照片中的瑕疵
三明:“两票制”堵住加价漏洞
漏洞在哪儿
高铁急救应补齐三漏洞
油罐车静态侧倾稳定角的多体仿真计算