原文链接: 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:

  1. 路径选择(path selection):从source点到sink点的执行路径上,可能会存在一些对参数的过滤净化等,如果选择可行的路径是难点之一;
  2. 持久性的数据库存储(persistent database state):对于二次SQL注入,存储型XSS等攻击,往往需要先将数据存入数据库,然后取出触发攻击,所以在生成exp时必须要先进行数据的存储操作。

作者实现了一个名为CHAINSAW的工具,并且在9个PHP应用程序上进行了测试,最后生成了199个exp。

1.1 Running example

论文中举了一个基于PHP的聊天室应用例子,存在几个因为未充分过滤导致的SQL注入漏洞:

1

room.php中,第12行对变量$room_name进行了过滤,过滤函数为mysql_real_escape_string(),在第17行将$room_name赋值为$_SESSION['room_name']。除此之外,参数$_POST['category']也未经过充分的过滤,会在第18行通过cat_desc传到dashboard.php中。

2

在dashboard.php中获取传进来的$cat_desc,然后在第11行传到addcat.php中。

3

在addcat.php中,存在两处sql调用:

  1. line 6,$room_name参数来自room.php,经过了mysql_real_escape_string函数的充分过滤,所以不存在sql注入漏洞;
  2. line 14,当$accesslevel为1的时候,$cat_desc在第11行通过$_GET请求获取,在第12行经过htmlspecialchars过滤,但是该函数在仅接受一个参数的情况下是不会对单引号进行过滤的,所以可以利用1' = '1' or '1进行sql注入。

4

同理,在第11行也存在一个sql注入漏洞,$sql经过$room_name拼接得到,但是没有经过充分的过滤。

mysql_real_escape_string会对敏感字符全部进行过滤:

8

但是htmlspecialchars如果参数设置不正确,就可能导致过滤不充分:

9

1.2 Exploit Generation Challenges

  1. Complex Workflow

可能会有多条从source点到sink点的路径(漏洞触发路径),而且路径会包含多个模块。这些路径包含开发者预期的执行流程,也有非预期的执行流程。

  1. Data Sanitization and Path Sensitivity

从source点sink点的路径上,数据可能可能会经过了充分的过滤,也可能未经过充分过滤而导致了漏洞。比如Listing 4中,如果第6行被执行,那么就不存在sql注入漏洞。所以需要构建模型,考虑从source点到sink点的不同的过滤,并生成一些sanitization无法防御的exp。

  1. Persistent Storage Effect

数据库中存储的数据也会影响到程序的控制流和数据流,比如在Listing 3中的$accesslevel就是从数据库中取出来的数据,会影响到后续的执行流程。

2 实验设计

目标:找到从source点到sink点的一串可利用的HTTP漏洞请求路径

总体流程如下图所示:

5

介绍一下总体流程

2.1 Seed Generation

目标:找到存在漏洞的sink点,并排除不存在漏洞的sink点。

将_exploit seed_定义为(S, I),其中:

  1. S表示一个sink点;
  2. I = {(i1, v1), … (in, in)}表示一系列的variable-value对,可以触发在S点的漏洞。

CHAINSAW会将从source点到sink点的执行路径符号化,得到一个符号化公式Fp,这个Fp表示了:

  1. 路径状态;
  2. 输入在执行路径上的变化;
  3. 在sink点上下文的约束。

然后还需要一个能够触发漏洞的一个payload字符串FA。最后生成的符号化公式为:

6

这一步会输出应用程序各个模块中存在的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表示从vivk的导航。

7

上图中边的权值表示从模块A跳转到模块B的全局路径数量(取log2值)。CHAINSAW会优先选择全局路径数量少的路径执行。比如上图中,从room.php到漏洞存在文件create.php一共有两条路径:

  1. room.php -> dashboard.php -> create.php,全局路径组合方式一共有2^10种,CHAINSAW会优先选择这条路径;
  2. room.php -> check.php -> create.php,全局路径组合方式一共有2^12种。

2.2.2 Refined Workflow Graph

10

上图中,方框中表示的是一个模块,如模块L2;在每个模块中又有一些node,每个node都表示一个local execution path(本地执行路径)。与之的对应的,global execution path(全局执行路径)为L2->A3->D2

前面有提到,对于每一个exploit seed,都对应着许多的global execution path,如果对这些全局执行路径都做一个遍历,那开销会很大。所以CHAINSAW通过删除一些不可行的边(路径)来进行剪枝操作。

具体的做法为(以上图为例):

  1. 首先,CHINSAW为每个顶点添加一个preconditions集合,比如上图中是模块L2,就是该模块内部执行需要满足的约束条件;
  2. 同时,CHAINSAW为每个顶点添加一个summary集合,主要来记录被赋值的全局变量,比如上图中被赋值的是$_SESSION['room_name']
  3. 接着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语句处的输入:

  1. 将对数据库进行写操作的query也认定为sink点,并生成从source点到该sink点的约束Fp
  2. 在该query语句处应该插入的值,记为约束FD
  3. 将FP ∧ 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为例:

11

第一行的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的执行流程为:

  1. 首先,CHINSAW为每个顶点添加一个preconditions集合和summary集合;
  2. 接着CHAINSAW发现D3的precondition检查isset($_SESSION['rname']),而R2的summary刚好满足precondition,所以接着CHAINSAW选择D3继续遍历;
  3. 同时,CHAINSAW将R2和D2的summary都加入summary history
  4. 接着CHAINSAW发现summary history中的内容满足C2中的precondition,所以会继续遍历节点C2;
  5. 最后整条路径就是R2->D3->C2。

15

3.3 Database Schema Analysis

在静态分析中,由于源代码中没有数据库约束,所以对持久存储的数据建立数据流依赖并不简单。所以CHAINSAW对database schema进行分析,从中提取中对于数据库的约束,比如数据表名,列名,列的类型,列值的约束(如NOT NULL,长度etc.)。

Listing 6为例,图中是一个insert语句,如果var1var2在数据表TBL中的数据类型为enum{1, 2},那么这条insert语句就不存在漏洞。

13

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点数量:

16

4.2 Setup

实验在一台双核2.4GHz,40GB RAM的Ubuntu 12.04 LTS VM上进行。对每个应用程序,先生成exploit seed,然后再生成具体的exp,也就是一串HTTP请求序列。

4.3 Overview of Results

下图是实验的总体结果:

17

对于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为例,模块之间的跳转为:

18

SQL注入点在viewmessage.php页面:

19

生成的exp为:

20

4.5 Selected Second-Order Exploits

二次注入以myBloggie为例:

21

首先在adduser.php中插入用户,在第2行因为对$user变量有不太充分的过滤导致了SQL注入漏洞,接着在edituser.php中取出该数据,拼接进Listing 15中的SQL查询语句进行查询:

22

exp:

23

4.6 Effect of Database Schema Analysis

作者分析了对数据库schema进行分析后给CHAINSAW带来的性能提升。如下图所示:

24

对database schema进行分析并提取约束后,在生成exp种子时的误报有了一定程度的减少,尤其是WebBid,false positive rate减少了将近60%。

4.7 Overhead and Analysis Complexity

作者对CHAINSAW的运行时开销进行了分析,结果如下表Table 4所示:

25

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需要分析。

作者对于CHAINSAW其他两种漏洞挖掘工具ArdillaCraxWeb进行了对比,发现CHAINSAW确实是能够找到更多的漏洞,生成更多的exp:

26

5 总体评价

CHAINSAW比较新颖的地方在于直接生成漏洞的exp,在静态分析的基础上,使用符号化执行,数据库schema分析等方法,提取出source点到sink点的约束并求解。但是对于一些前端的js约束考虑的不是很充分,这个问题在作者发表在USENIX Security’18的NAVEX中得到了解决。

6 思维导图

Chainsaw