Tai-e 实现从某一个类全部方法到sink全污点路径输出
0x01 前言
要做一个输出污点路径进行fuzz 插桩的功能,doop不太好做,转头想到tai-e就拿来实现一下
0x02 tai-e 插件编写
tai-e本身是没有这种功能的,但是可以通过插件来实现自己想要的分析算法,本次需要的算法只需要在污点分析的基础上做即可,先分析出需要完成的工作:
- 定义entryPoint
- 定义VarPointsTo
具体的细节就不进行分析了,下面这篇文章写的很好
使用太阿(Tai-e)进行静态代码安全分析(spring-boot篇一) - 先知社区
如果想进一步了解有关分析插件系统的更多详细信息可以看如下论文的第 4.1 节和源代码
Tai-e: A Developer-Friendly Static Analysis Framework for Java by Harnessing the Good Designs of Classics | Proceedings of the 32nd ACM SIGSOFT International Symposium on Software Testing and Analysis
先创建一个基础的框架
package pascal.taie.analysis.pta.plugin.taint;
import pascal.taie.analysis.pta.plugin.Plugin;
public class Class2SinkHandler implements Plugin {
private Solver solver;
private TaintManager manager;
private static final String[] CLASS_NAMES = {
};
@Override
public void setSolver(Solver solver) {
this.solver = solver;
manager = new TaintManager(solver.getHeapModel());
}
@Override
public void onStart() {
}
@Override
public void onNewCSMethod(CSMethod csMethod) {
}
}
先需要在onStart方法中添加我们需要的EntryPoint,首先获取到所有的class,判断是否为我们需要的class,如果是的话将其加入EntryPoint
@Override
public void onStart() {
List<JClass> classes = solver.getHierarchy().applicationClasses().toList();
for (JClass jClass : classes) {
if (containsAny(jClass.getName(), CLASS_NAMES)) {
addEntryPoints(jClass);
}
}
}
private void addEntryPoints(JClass jClass) {
jClass.getDeclaredMethods().forEach(method -> {
if (!method.isAbstract()) {
solver.addEntryPoint(new EntryPoint(method, EmptyParamProvider.get()));
}
});
}
到这完成了entryPoint的添加,接下来需要将指定类中所有的方法参数作为source进行污点传播
@Override
public void onNewCSMethod(CSMethod csMethod) {
JMethod method = csMethod.getMethod();
if (isCommitMethod(method)) {
processCSMethod(csMethod);
}
}
private boolean isCommitMethod(JMethod method) {
for (String className : CLASS_NAMES) {
if (method.getRef().toString().contains(className)) {
return true;
}
}
return false;
}
private void processCSMethod(CSMethod csMethod) {
JMethod method = csMethod.getMethod();
Context context = csMethod.getContext();
IR ir = method.getIR();
for (int i = 0; i < ir.getParams().size(); i++) {
Var param = ir.getParam(i);
SourcePoint sourcePoint = new ParamSourcePoint(method, new IndexRef(IndexRef.Kind.VAR, i, null));
Obj taint = manager.makeTaint(sourcePoint, param.getType());
solver.addVarPointsTo(context, param, taint);
}
System.out.println("inNewCSMethod: " + method.getRef().toString());
}
简单解释这个代码,CSMethod中存储的是所有的方法,通过获取该方法的Ref,即如下格式
<com.thoughtworks.xstream.XStream: java.lang.Object fromXML(java.io.InputStream)>
Ref中会保存有该方法所位于类的类名,通过判断Ref是否包含需处理的类名来确定方法,在确定了方法之后获取全部的参数,将其组成一个sourcePoint污点加入VarPointsTo进行分析
在PointerAnalysis#setPlugin直接加入该插件,然后执行程序进行分析
0x03 测试
在上述代码编写完成后需要在yml文件中添加一下sink
sources:
# - { kind: param, method: "<org.joychou.controller.SQLI: java.lang.String jdbc_sqli_sec(java.lang.String)>", index: 0}
sinks:
## SQLI
- { vuln: "SQL Injection", level: 4, method: "<java.sql.Statement: java.sql.ResultSet executeQuery(java.lang.String)>", index: 0 }
transfers:
- { method: "<java.lang.String: java.lang.String concat(java.lang.String)>", from: base, to: result }
- { method: "<java.lang.String: java.lang.String concat(java.lang.String)>", from: 0, to: result }
- { method: "<java.lang.String: char[] toCharArray()>", from: base, to: result }
- { method: "<java.lang.String: void <init>(char[])>", from: 0, to: base }
- { method: "<java.lang.String: void getChars(int,int,char[],int)>", from: base, to: 2 }
- { method: "<java.lang.String: java.lang.String format(java.lang.String,java.lang.Object[])>", from: "1[*]", to: result }
- { method: "<java.lang.StringBuffer: void <init>(java.lang.String)>", from: 0, to: base }
- { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>", from: 0, to: base }
- { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>", from: 0, to: result }
- { method: "<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>", from: base, to: result }
- { method: "<java.lang.StringBuffer: java.lang.String toString()>", from: base, to: result }
- { method: "<java.lang.StringBuilder: void <init>(java.lang.String)>", from: 0, to: base }
- { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: 0, to: base }
- { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: 0, to: result }
- { method: "<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>", from: base, to: result }
- { method: "<java.lang.StringBuilder: java.lang.String toString()>", from: base, to: result }
- { method: "<java.io.InputStreamReader: void <init>(java.io.InputStream,java.lang.String)>", from: 0, to: base }
- { method: "<java.io.InputStreamReader: void <init>(java.io.InputStream,java.lang.String)>", from: 0, to: result }
- { method: "<java.io.InputStreamReader: void <init>(java.io.InputStream,java.nio.charset.Charset)>", from: 0, to: base }
- { method: "<hudson.util.Secret: java.lang.String getPlainText()>", from: base, to: result }
- { method: "<java.io.InputStreamReader: void <init>(java.io.InputStream)>", from: 0, to: base }
# - { method: "<java.io.InputStreamReader: void <init>(java.io.InputStream,java.nio.charset.Charset)>", from: 0, to: result }
# - { method: "<java.io.InputStreamReader: void <init>(java.io.InputStream,java.nio.charset.Charset)>", from: base, to: result }
call-site-mode: true
source无需编写,上述插件会动态添加,运行即可输出污点流结果