原文链接: https://sisl.rites.uic.edu/static/pics/fp0612-alhuzali.pdf
本文发表在CCS’16,第一作者是来自伊利诺伊大学芝加哥分校的Abeer Alhuzali。该作者还在USENIX Security’18上发表了一篇NAVEX,是对本次工作Chainsaw的改进。
1 主要内容
Web漏洞挖掘主要有两种方式,一种是黑盒的penetration-testing,另一种是白盒的程序分析。在本文中,作者提出了一种基于静态分析的漏洞exp生成方法。主要有两个Challenge:
- 路径选择(path selection):从source点到sink点的执行路径上,可能会存在一些对参数的过滤净化等,如果选择可行的路径是难点之一;
- 持久性的数据库存储(persistent database state):对于二次SQL注入,存储型XSS等攻击,往往需要先将数据存入数据库,然后取出触发攻击,所以在生成exp时必须要先进行数据的存储操作。
作者实现了一个名为CHAINSAW的工具,并且在9个PHP应用程序上进行了测试,最后生成了199个exp。
1.1 Running example
论文中举了一个基于PHP的聊天室应用例子,存在几个因为未充分过滤导致的SQL注入漏洞:
在room.php
中,第12行对变量$room_name
进行了过滤,过滤函数为mysql_real_escape_string()
,在第17行将$room_name
赋值为$_SESSION['room_name']
。除此之外,参数$_POST['category']
也未经过充分的过滤,会在第18行通过cat_desc
传到dashboard.php
中。
在dashboard.php中获取传进来的$cat_desc
,然后在第11行传到addcat.php
中。
在addcat.php中,存在两处sql调用:
- line 6,
$room_name
参数来自room.php
,经过了mysql_real_escape_string
函数的充分过滤,所以不存在sql注入漏洞; - line 14,当
$accesslevel
为1的时候,$cat_desc
在第11行通过$_GET请求获取,在第12行经过htmlspecialchars
过滤,但是该函数在仅接受一个参数的情况下是不会对单引号进行过滤的,所以可以利用1' = '1' or '1
进行sql注入。
同理,在第11行也存在一个sql注入漏洞,$sql经过$room_name
拼接得到,但是没有经过充分的过滤。
mysql_real_escape_string会对敏感字符全部进行过滤:
但是htmlspecialchars如果参数设置不正确,就可能导致过滤不充分:
1.2 Exploit Generation Challenges
- Complex Workflow
可能会有多条从source点到sink点的路径(漏洞触发路径),而且路径会包含多个模块。这些路径包含开发者预期的执行流程,也有非预期的执行流程。
- Data Sanitization and Path Sensitivity
从source点sink点的路径上,数据可能可能会经过了充分的过滤,也可能未经过充分过滤而导致了漏洞。比如Listing 4中,如果第6行被执行,那么就不存在sql注入漏洞。所以需要构建模型,考虑从source点到sink点的不同的过滤,并生成一些sanitization无法防御的exp。
- Persistent Storage Effect
数据库中存储的数据也会影响到程序的控制流和数据流,比如在Listing 3中的$accesslevel
就是从数据库中取出来的数据,会影响到后续的执行流程。
2 实验设计
目标:找到从source点到sink点的一串可利用的HTTP漏洞请求路径
总体流程如下图所示:
介绍一下总体流程
2.1 Seed Generation
目标:找到存在漏洞的sink点,并排除不存在漏洞的sink点。
将_exploit seed_定义为(S, I)
,其中:
- S表示一个sink点;
- I = {(i
1, v1), … (in, in)}表示一系列的variable-value对,可以触发在S点的漏洞。
CHAINSAW会将从source点到sink点的执行路径符号化,得到一个符号化公式Fp,这个Fp表示了:
- 路径状态;
- 输入在执行路径上的变化;
- 在sink点上下文的约束。
然后还需要一个能够触发漏洞的一个payload字符串FA。最后生成的符号化公式为:
这一步会输出应用程序各个模块中存在的exploit seed列表。
在这一步还有一个问题待解决, 全局变量,比如$_SESSION
,它可能在当前模块被赋值,也可能是其他模块,所以CHAINSAW需要找到给全局变量赋值的模块 。
2. 2 Navigation Problem
对模块MN给定一个exploit seed,是否存在一个navigation sequence(M1, …, MN就是一些列的HTTP请求序列,也被称为global execution path
,全局执行路径)满足exploit seed中的指定约束,从而触发漏洞?
这其实是CHAINSAW比较难以解决的一个问题。对于每一个exploit seed,都对应着许多的global execution path,如果对这些全局执行路径都做一个遍历,那开销会很大。所以CHAINSAW的解决办法对这些navigation sequence进行排序,排序依据是全局执行路径的组合数量,据此选择最有可能的navigation sequence。
具体来说分为两步,首先CHAINSAW构建了一个General Workflow Graph(通用工作流图),表示所有的navigation sequences以及这些导航序列的优先级;接着对于每一个navigation graph构造一个Refined Workflow Graph(优化工作流图)来寻找可以成功触发漏洞的全局执行路径。
2.2.1 Workflow Inference
General Workflow Grpah(GWFG)是一个有向加权图,G=(V, E)。其中v ∈ V,表示程序的某一个模块; e = (vi, vk) ∈ E表示从vi到vk的导航。
上图中边的权值表示从模块A跳转到模块B的全局路径数量(取log2值)。CHAINSAW会优先选择全局路径数量少的路径执行。比如上图中,从room.php到漏洞存在文件create.php一共有两条路径:
- room.php -> dashboard.php -> create.php,全局路径组合方式一共有2^10种,CHAINSAW会优先选择这条路径;
- room.php -> check.php -> create.php,全局路径组合方式一共有2^12种。
2.2.2 Refined Workflow Graph
上图中,方框中表示的是一个模块,如模块L2;在每个模块中又有一些node,每个node都表示一个local execution path(本地执行路径)。与之的对应的,global execution path(全局执行路径)为L2->A3->D2
。
前面有提到,对于每一个exploit seed,都对应着许多的global execution path,如果对这些全局执行路径都做一个遍历,那开销会很大。所以CHAINSAW通过删除一些不可行的边(路径)来进行剪枝操作。
具体的做法为(以上图为例):
- 首先,CHINSAW为每个顶点添加一个preconditions集合,比如上图中是模块L2,就是该模块内部执行需要满足的约束条件;
- 同时,CHAINSAW为每个顶点添加一个summary集合,主要来记录被赋值的全局变量,比如上图中被赋值的是
$_SESSION['room_name']
; - 接着CHAINSAW发现A3的precondition检查
isset($_SESSION['room_name'])
,而L2的summary满足A3的precondition,所以A3是可行的路径,CHAINSAW会优先选择A3进行遍历。
2.3 Second Order Exploits
另一个需要解决的是二次攻击利用的问题,在应用程序中还存在一些二次SQL注入漏洞和存储型XSS漏洞。数据被存入数据库的状态直接影响到漏洞能否被触发。
Static Input Generation.
首先是静态输入生成。在一些情况下,从source点到sink点的漏洞触发依赖于数据库的状态,比如在Listing 3中,line 13处的select语句能否被执行依赖于line 5的insert语句被成功执行。所以在触发select语句处的sql漏洞之前,还需要执行对数据库的写操作。为此,CHAINSAW建立了对同一张数据表进行读写操作语句的映射,写操作指insert语句和update语句,读操作指select语句。然后CHAINSAW会生成满足触发漏洞要求的insert语句处的输入:
- 将对数据库进行写操作的query也认定为sink点,并生成从source点到该sink点的约束F
p; - 在该query语句处应该插入的值,记为约束F
D; - 将F
P∧ FD送到约束求解器中求解。
对于二次攻击利用的漏洞,CHAINSAW会寻找两个sink点,第一处是写操作query语句(如insert或者update),另一个处是查询的query语句,从数据库中读取的数据没有经过正确的过滤净化导致的漏洞。
3 工具实现
工具:作者使用Pixy和TAPS来进行静态分析,利用Z3来求解约束。
输入:web application source,its database schema,the attack specifications(就是一些可以触发漏洞的字符串,比如xss就是<script>alert(xss);</script>
)。
输出:从Public Page公共页面到触发漏洞的一系列HTTP请求序列。
3.1 Context awareness
3.1.1 Symbolic Parsers.
CHAINSAW会将从source点到sink点的执行路径全部符号化,并给出一个符号化的表述。为了能够得到这些符号表达式中用户输入,作者在SQL解析器和HTML解析器上实现了两个附加的(符号)解析器。主要目的是识别在sink点的变量类型和过滤函数。
3.1.2 Context awareness example.
CHAINSAW会根据上下文的语义,选择合适的攻击向量,以Listing 5为例:
第一行的echo对应的$input的payload应为:
1 | "<script>alert(’xss’)</script>" |
第2行则需要闭合标签中的>
符号,所以payload为:
1 | "’><script>alert(’xss’)</script>" |
CHAINSAW中符号解析器能够推断这些不同的上下文,并构建出相应的求解公式,该公式被发送给求解器求解。
3.2 Analysis of Navigation Structure
这一步的目的是生成GWFG图和RWFG图。
3.2.1 生成通用工作流图 General workflow graph
CHAINSAW利用HTML Parser和CFG图提取出工作流转换的特征,入HTTP链接,表单,meta tags,iframes和PHP重定向函数。提取出这些特征之后,CHAINSAW创建两个节点,分别表示工作流特性的源(source)模块和目标(sink)模块。
3.2.2 生成优化工作流图 Refined Workflow graph
在实现refined workflow graph时,CHAINSAW引入了新的机制,summary history,比如对于下面这个例子,CHAINSAW的执行流程为:
- 首先,CHINSAW为每个顶点添加一个preconditions集合和summary集合;
- 接着CHAINSAW发现D3的precondition检查
isset($_SESSION['rname'])
,而R2的summary刚好满足precondition,所以接着CHAINSAW选择D3继续遍历; - 同时,CHAINSAW将R2和D2的summary都加入summary history;
- 接着CHAINSAW发现summary history中的内容满足C2中的precondition,所以会继续遍历节点C2;
- 最后整条路径就是R2->D3->C2。
3.3 Database Schema Analysis
在静态分析中,由于源代码中没有数据库约束,所以对持久存储的数据建立数据流依赖并不简单。所以CHAINSAW对database schema进行分析,从中提取中对于数据库的约束,比如数据表名,列名,列的类型,列值的约束(如NOT NULL,长度etc.)。
以Listing 6为例,图中是一个insert
语句,如果var1
和var2
在数据表TBL中的数据类型为enum{1, 2}
,那么这条insert语句就不存在漏洞。
CHAINSAW会database schema中提取出TBL表的约束,($v1==1 ∨ $v1==2) and ($v2==1 ∨ $v2==2)
加入约束求解公式求解。数据库的分析模块支持多种数据库,如MSSQL,MySQL和Oracle等。
3.4 Constraint Solving
CHAINSAW利用Z3 SMT和Z3-str进行约束求解。
4 实验评估
4.1 Dataset
作者利用CHAINSAW对9个PHP应用程序进行了测试,代码行数跨度从323行到65302不等。表中列出了PHP应用的文件数,SQL查询语句数,数据表数量以及XSS的sink点数量:
4.2 Setup
实验在一台双核2.4GHz,40GB RAM的Ubuntu 12.04 LTS VM上进行。对每个应用程序,先生成exploit seed,然后再生成具体的exp,也就是一串HTTP请求序列。
4.3 Overview of Results
下图是实验的总体结果:
对于9个应用程序,CHAINSAW共生成了181个exp种子和199个exp,并且不存在误报。其中149(75%)是SQLI的exp,50(25%)是XSS的exp。在所有的exp中,30(15%)是属于二次攻击的exp,其中13个exp是二次注入。
4.4 Selected First-Order Exploits
以webchess为例,模块之间的跳转为:
SQL注入点在viewmessage.php
页面:
生成的exp为:
4.5 Selected Second-Order Exploits
二次注入以myBloggie为例:
首先在adduser.php
中插入用户,在第2行因为对$user
变量有不太充分的过滤导致了SQL注入漏洞,接着在edituser.php
中取出该数据,拼接进Listing 15中的SQL查询语句进行查询:
exp:
4.6 Effect of Database Schema Analysis
作者分析了对数据库schema进行分析后给CHAINSAW带来的性能提升。如下图所示:
对database schema进行分析并提取约束后,在生成exp种子时的误报有了一定程度的减少,尤其是WebBid,false positive rate减少了将近60%。
4.7 Overhead and Analysis Complexity
作者对CHAINSAW的运行时开销进行了分析,结果如下表Table 4所示:
analysis time指的是从生成exp种子开始到生成送到求解器中求解的约束为止,时间跨度从10s到600s。solving time是指求解最后的final exploit formula的时间,对于小型应用程序,如_geccbblite_的约束求解时间甚至不到1s,而相对大型的应用程序_WebBid_则花费了42s。对于_WebBid_,_schoolmate_,_webchess_,分析以及求解约束的时间都比较长,是因为对应的execution path很多,比如_WebBid_有1.09 mllion的execution paths需要分析。
4.8 Comparison with Related Work
作者对于CHAINSAW其他两种漏洞挖掘工具Ardilla和CraxWeb进行了对比,发现CHAINSAW确实是能够找到更多的漏洞,生成更多的exp:
5 总体评价
CHAINSAW比较新颖的地方在于直接生成漏洞的exp,在静态分析的基础上,使用符号化执行,数据库schema分析等方法,提取出source点到sink点的约束并求解。但是对于一些前端的js约束考虑的不是很充分,这个问题在作者发表在USENIX Security’18的NAVEX中得到了解决。