本文首发于跳跳糖
Introduction
本文主要是根据ptaint这篇论文的思想:将指针分析和污点分析统一起来进行分析,实现ByteCodeDL的污点分析功能,在阅读本文之前建议先学习下面三份资料
- https://yanniss.github.io/ptaint-oopsla17-prelim.pdf
- https://pascal-group.bitbucket.io/lectures/Security.pdf
污点分析:which sources can reach which sinks
指针分析:which object sources can reach which variables
指针分析是计算指针在运行过程中可能指向哪些对象,也可以理解为创建之后的对象,会传播到哪些指针
1 | A a = new A("foo"); // object created |
污点分析是计算sink函数的参数是否是污点,也可以理解为污点源会传播到哪些指针
1 | String a = source.readLine(); // taint source |
两者可以统一成,值在指针之间的传播,也就是在PFG(Pointer Flow Graph)中传播
污点分析和指针分析相比还多了一些东西,比如污点转移,以及污点消除(净化函数)
1 | String a = source.readLine(); // taint source |
Ptaint论文中将污点视为独立的对象,而不是给传统的对象打上污点标记,会创建新的污点对象,和传统的对象分开各自独立沿着相同的PFG传播
按照论文中的给的示例规则得到的实现
1 | .comp PTaint{ |
对应的使用规则:ptaint-example-1.dl
1 | #include "inputDeclaration.dl" |
可以看到大部分的规则都是和指针分析一样的,那么能不能利用之前实现的pt-noctx.dl呢?答案是可以的,但是有一处要稍微改一下
1 | // param |
更改后的pt-noctx.dl
更新后的ptaint.dl
1 | #include "pt-noctx.dl" |
Example 1
我们先分析Benchmark中的TaintDemo3
1 | public class TaintDemo3 { |
执行
1 | // 代码下载到本地 |
然后在ouput目录能够看到grep "Demo3" TaintVar.csv
结果
1 | <com.bytecodedl.benchmark.demo.TaintDemo3: void test1(java.lang.String)>/@parameter0 |
可以看到sql1和sql0都是污点,sql不是污点,比较符合我们的预期。
下面介绍如何编写出ptaint-example-1.dl
首先要实例化Ptaint 并 初始化污点分析的起始方法,在这里起始方法,也就是分析的入口,为TaintDemo3的main函数
1 | // 实例化ptaint |
然后定义Source,Sink和Sanitize函数
1 | // 定义source函数 |
接下来就是定义污点转移函数了,这时候只看java代码看不出来东西,这时候需要看soot生成的jimple代码,也就是下面的代码
1 | public void test1(java.lang.String) |
也就是将字符串的拼接分成了下面几步
1 | $stack5 = new java.lang.StringBuilder; |
转换成java就是
1 | $stack5 = new StringBuilder(); |
name#_0 是污点变量,由于$stack7 = $stack6.append(name#_0);
,我门希望$stack7
也是污点变量,所以这里应该添加个arg 向 ret的转移。
1 | ptaint.ArgToRetTransfer("<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", 0). |
由于$stack7
是污点变量,由于$stack8 = $stack7.append("\'");
,我们希望$stack8
也是污点变量,所以这里应该添加个base 向 ret的转移。
1 | ptaint.BaseToRetTransfer("<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>"). |
由于$stack8
是污点变量,由于$sql0#_11 = $stack8.toString();
,我们希望$sql0#_11
也是污点变量,所以这里应该添加个base 向ret的转移。
1 | ptaint.BaseToRetTransfer("<java.lang.StringBuilder: java.lang.String toString()>"). |
全部规则如下: ptaint-example-1.dl
1 | #include "inputDeclaration.dl" |
但是在分析TaintDemo2的时候,就会发现遇到问题了
1 | public class TaintDemo2 { |
对应的jimple为
1 | public void test1(java.lang.String) |
将ptaint-example-1.dl中的TaintDemo3换成TaintDemo2之后后
1 | <com.bytecodedl.benchmark.demo.TaintDemo2: void test1(java.lang.String)>/@parameter0 |
会发现sql#_11
虽然经过了Sanitize处理,但是还是被标记为了污点变量,这是为什么呢?这是因为目前的分析都是流不敏感的,有没有什么办法解决呢?有在创建facts时加上--ssa
参数
1 | java8 -jar ~/code/soot-fact-generator/build/libs/soot-fact-generator.jar -i Benchmark-1.0-SNAPSHOT.jar --full -l /Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/rt.jar -d tainttest --allow-phantom --generate-jimple --ssa |
加上这个参数之后会生成shimple,保证每个变量只会被赋值一次,就会变成下面这样
1 | public void test1(java.lang.String) |
原本Sanitize返回的也是sql#_11
现在变成了sql_$$A_1#_12
,这样就能区分原本两个同名变量在不同时刻的值了。
Example 2
在实际的场景中,比如spring开发框架下,污点源不是来自source函数的返回值,可能来自函数的参数,这种情况该怎么处理呢?
我们还以TaintDemo3为例,我们以test1方法为分析的起点,形式参数name作为污点源。这时候我们需要模拟创建对象,包括test1@this 以及name参数
1 | .decl EntryMethod(method:Method) |
其他部分同ptaint-example-1.dl,完整见ptaint-example-2.dl
1 | #include "inputDeclaration.dl" |