一种基于数据依赖的污点分析方法
2022-09-29周轩
周轩
(吉安职业技术学院,江西吉安 343000)
1 引言
随着网络安全形势日益严峻,Web网站正面临各类网络安全攻击。根据OWASP在2021年发布的十大Web应用程序漏洞中指出,注入攻击排在第三位,足以说明其威胁性非常大。常见的注入攻击包括SQL注入和跨站脚本攻击(XSS),也有代码注入、命令注入、CCS注入等,遭受攻击的主要原因是没有对外部输入的数据进行验证和过滤、直接使用或连接恶意数据等,这类数据称之为污点源。在Web网站开发时,如果程序员对可疑的外部输入数据进行验证,此类攻击事件的发生概率将会非常小[1]。因此,对Web应用程序进行污点分析,找出所有未验证的污点源是非常必要的。
程序变量的数据依赖关系在程序分析中起到重要的作用,许多编译优化和数据流分析方法就需要使用经典的变量定义使用链(DU),DU链是一种方法内的数据依赖关系。基于需求的数据依赖图是指程序中“有关”变量的所有数据依赖关系构成的一张图,这里的“有关”变量由用户输入指定,当“有关”变量指程序中出现的所有变量时,即为程序中所有数据依赖关系[2]。
污点分析方法通常分为动态污点分析和静态污点分析。动态污点分析是在程序的运行过程中对可疑的输入源进行标记及跟踪,观察其在程序运行过程中的传播路径,判断是否会对程序产生不良影响,以此判断是否为污点输入源[3]。静态污点分析则是在不运行和不修改代码的情况下,对程序进行语句分析,运用程序变量之间的数据依赖关系,找出污点源(Sources)和污点汇聚点(Sink)之间的传播路径,从而达到找出程序潜在漏洞隐患的目的,再通过验证数据方法进行无害化处理(Sanitizer)[4]。
本文基于Soot字节码分析工具将J2EE程序转换成Jimple中间表示,运用其生成的函数调用关系图(Call Graph)、数据依赖关系及指针分析工具,建立Sources和Sink的可达矩阵,从而得到污点传播路径,为漏洞修复提供参考依据。
2 相关语法及定义
2.1 Jimple中间表示介绍
Soot是一种开源的Java字节码优化框架,其本身也是使用Java语言开发,可以将使用Java语言编写的程序转换为Soot的中间表示,其表示形式有Baf、Jimple、Shimple和Grimp,其中Jimple是Soot字节码分析工具最主要使用的一种中间代码,可以将Java程序中的复杂调用、计算等语句转化为三地址的呈现方式,只保留15种不同语句类型,简化程序表示方式,方便开展代码优化及分析[5]。
2.2 方法涉及的语句及参数类型
Soot是一个开源框架,在转译J2EE程序为Jimple中间表示过程中,会根据代码中不同类型元素生成相应的类与对象及函数调用关系图(Call Graph)。根据静态分析需求,此方法分析涉及语句主要有函数调用语句(带返回值与不带返回值)、函数的入口语句及返回语句、赋值语句,Sources、Sink均包含于函数调用语句。为增加污点分析精度,在分析数据依赖传递时加入域访问变量数据的检测,确保访问对象内部变量时不遗漏数据依赖传递关系[6-8]。
2.3 方法相关定义
定义1:字符串类型或对象为污点传播类型,包括String、StringBuffer、StringBuilder、String[]、char[]等。
定义2:Sink语句为系统所有输出类语句,输出对象为污点传播类型。
定义3:Source语句为系统所有输入类语句。
定义4:当一个对象v出现在赋值语句s等号右边或函数返回语句s返回对象时,则Use(s,v)成立。
定义5:当一个对象v出现在赋值语句t等号左边或为函数入口语句t的形参,则Def(t,v)成立。
定义6:同一方法内,相同变量之间存在依赖关系。
定义7:当两个不同对象v1和v2同时指向同一内存地址,则v1和v2之间存在依赖关系。
定义8:函数调用返回值与函数内返回语句返回对象存在依赖关系。
定义9:函数调用参数与函数内入口形参之间存在依赖关系。
定义10:语句s和t为两条不同语句,Use(s,v1)、Def(t,v2)成立,且v1和v2存在依赖关系,则s和t之间存在一条数据传递路径。
3 方法描述
3.1 污点分析方法框架
污点分析流程图如图1所示,具体步骤如下:
(1)使用Soot工具将要分析的J2EE源程序或其编译的Class文件转换为Jimple中间表示,在转换的过程中加入指针分析,增加数据依赖分析精度。
(2)结合定义的Sink和Source语句特征找出程序中符合条件的所有语句,建立初始依赖图。
(3)对所有Sink语句根据数据传递依赖关系,进行方法内和跨方法计算,建立数据依赖图。
(4)根据数据依赖图,建立图中节点之间的可达矩阵,找出所有Sink与Source的可达性,从而得到污点传播路径,并进行漏洞修复。
3.2 数据依赖图初始化
根据定义1、2、3,Sink和Source定义的对象类型,在Soot进行字节码转换时,找出符合条件的对象和所在语句,分别添加至Sink和Source列表中,并将所有Sink语句添加至方法内计算队列,后续分析将根据队列中的Sink点进行依赖传递分析,找出Sink与Source的传递路径。
根据定义7,对于不同方法中不同变量之间存在依赖关系,在初始化时将变量所在的语句直接建立依赖边,可以减少数据分析的资源消耗。
3.3 方法内计算依赖边
从方法内计算队列中取出一条语句s1及污点传播对象v1,根据定义4、5、10,在语句方所在方法内部进行数据依赖传递分析。
在计算时,根据s1中的污点传播对象v1,在方法内向上逐条扫描语句,当出现语句t1满足Def(t1,v1)时,则建立一条语句s1到t1的依赖边。如t1语句为普通赋值语句,将语句t1加入方法内计算队列。如t1为函数调用语句,则将t1添加至跨方法计算队列。
当方法内计算队列所有语句全部处理完毕后,开始进行跨方法计算。
3.4 跨方法计算依赖边
在跨方法计算队列中取出一条语句s2及污点传播对象v2,根据定义7、8、9,建立s2与调用方法内相关语句的依赖关系。
在计算时,如调用方法有返回值,则建立一条s2与返回值所在语句t2的依赖边,并将t2添加至方法内计算队列。如调用方法没有返回值,则查找是否在数据依赖图初始化时已经建立跨方法的数据依赖边,如已建立,则将依赖边t3取出,添加至方法内计算队列,否则,结束当前语句依赖边计算。
当调用方法为库函数时,如进入库方法进行计算,将花费大量的时间。为提高计算效率,对于库方法调用时可直接建立依赖边,并将语句涉及的所有变量均定义为五点传播对象。此方法将导致后续进行污点路径分析时有误报情况,需进行人工核对,筛选出误报路径。
当跨方法计算队列所有语句全部处理完毕时,返回执行方法内计算队列进行依赖边建立。
3.5 特殊处理
在对程序进行静态分析的时候处理的种子变量主要为可变类型。由于Java中可变类的构造方法是将字符串保存在可变类中私有变量value[]数组中,在跨方法计算时往往迭代到可变类的方法中value[]的初始化,而不能返回到方法调用语句。例如sb.appand(s)语句,其中sb为StringBuffer类型,s为String类型,按照算法定义是sb被s修改,因此sb与s之间存在数据依赖关系。但是在算法实现中执行到该方法调用语句时,将进行跨方法计算,最终将计算到sb类的域变量value[],导致结果达不到预期的期望。对于这种情况,本文采取假定这些方法都是闭包,没有任何副作用,它们不影响任何的全局变量,仅仅影响方法的参数和返回值。原迭代算法照常进行的基础下,在处理方法调用时,若种子变量为可变类型,则认为其可被方法调用语句中其他可变类型参数修改。
3.6 污点路径查找与修复
当方法内计算与跨方法计算队列均执行完毕时,完整的污点数据依赖图完成建立。污点路径查找最简单的方法可以通过每个Sink语句,在数据依赖图中进行深度优先搜索,当执行到最深处时,假如该节点为Source,则找到一条有序的污点路径。
通过实验分析,上述方法在执行小型web网站时可以取得很好的效果,但在分析大型网站所消耗的时间呈几何增长。针对路径过多的问题,可将路径划分为4部分:Sink、临界点1、临界点2、Source。临界点定义为所在边的两条语句中一条为web程序代码,另一条为库方法中的语句,选取web程序代码作为临界点。临界点1和临界点2分别为离Sink和Source最近的进入库方法语句和出库方法语句。
为了找出路径中的临界点,可使用标准Warshall算法对数据依赖图进行预处理,建立可达性矩阵,在矩阵中标记所有节点互相的可达性。为节省内存,可将int除去符号位拆分成31位,用来标记节点是否可达。
根据可达性矩阵找出Sink所在行,查看Sink的可达节点中是否有Source,有则说明Sink和Source之间存在至少一条污染路径。由于Sink和Source之间路径经过节点过多,使用遍历的方法找出路径当中离Sink和Source最近的临界点(边的两个节点中一个节点为应用程序中的语句,另一个节点为库方法中的语句),使用Sink、临界点1、临界点2、Source来表示一条大概的污点路径。
找到路径之后,可以在Source和Sink语句之间添加验证函数来修复该漏洞。例如SQL注入攻击,在输入用户名和密码之后未对其进行验证,系统将会进行报警,待验证之后即修复了该潜在漏洞。
4 结语
本文借助Soot字节码分析工具提出了一种基于数据依赖的污点分析方法,以Sink开始计算,通过数据依赖关系及指针分析进行方法内及跨方法数据传播依赖边的建立,形成基于Sink的数据依赖图,进而找出潜在的安全漏洞。下一步将完善Sink和Source定义,进一步优化算法,提高分析准确性,减少误报率。