打算学一下codeql,所以对AST和污点分析要有个大概的理解和知道,内容还应该有很多,但是打算codeql边学边学习AST和污点分析,所以先把一些概念性的东西以及如何使用理清楚,内容不多,主要是写一点然后开始尝试去实战codeql慢慢学习。
污点分析
https://www.k0rz3n.com/2019/03/01/%E7%AE%80%E5%8D%95%E7%90%86%E8%A7%A3%E6%B1%A1%E7%82%B9%E5%88%86%E6%9E%90%E6%8A%80%E6%9C%AF/
污点分析
简介 污点源:不受信任的数据来源
汇聚点:产生了敏感数据操作
无害处理:通过对数据处理或者移除危险操作使数据流变的安全
个人理解就是污点源就是用户可控的数据源,汇聚点就是漏洞触发点,无害处理就是通过对可控数据处理或者对漏洞触发点处理。导致漏洞被修复或者无法利用。
污点分析的流程就是白话文就是对接受用户数据以后,分析对数据或者触发流程进行了无害处理,如果未进行则会产生漏洞,如果处理了则不会产生漏洞的这个过程。
流程分析
一:识别污点源
识别污点源的办法,根据参考文章来看,大概有三类
(1)外部程序传参
(2)应用程序调用的API和数据类型手工标记
(3)机器学习+统计
二:污点传播分析
污点传播分析可以理解为数据流向,分为显示和隐式分析,我们从下图的代码入手进行分析
污点源 a
汇聚点 add
无害处理 wuhai
real 其中传入的参数a,而b能够因为a造成数据依赖影响,所以为显示影响
yin 其中传入的参数a,不会直接对b造成数据依赖影响,但是由于a的循环,导致b也会变化,所以为隐式分析。
三 无害处理
简单来说就是把不安全的数据变的安全的处理。比如实体编码,过滤函数等。上图中就是保证数据小于或者等于10;
四 污点传播分析技术
分为静态和动态污点传播分析
静态分析 无需更改或者运行代码,通过分析程序间的数据依赖来检测数据的流向。
动态分析 就是程序运行过程中通过对污点数据监控来看能否达到污点汇聚点 。
静态分析
import java.io.IOException;
public class testRCE {
String cmd;
public testRCE(String cmd){
this.cmd=cmd;
}
private static String filter(String cmd){
if (cmd.equals("calc")){
cmd ="";
}
return cmd;
}
public void exec() throws IOException {
filter(this.cmd);
Runtime.getRuntime().exec(this.cmd);
}
public static void main(String[] args) throws IOException {
String cmd = "notepad.exe";//模拟从web端获取到的输入
testRCE t = new testRCE(cmd);
t.exec();
}
}
我们用一段代码来分析 上图比较能够分辨出来
污点源 cmd
无害处理 filter
我们分析污点传播,当我们获取到了输入,污点标记通过testRCE()赋值给了testRCE.cmd,然后通过无害处理,最终走到了污点汇聚exec()方法处,调用了 Runtime.getRuntime().exec(this.cmd)导致了命令执行漏洞。
AST抽象语法树
https://www.geeksforgeeks.org/abstract-syntax-tree-ast-in-java/
https://houbb.github.io/2020/05/29/java-ast-01-javaparser
https://houbb.github.io/2020/05/29/java-ast-05-javaparser-flying-visit
解释
AST 主要用于编译器以检查代码的准确性。如果生成的树有错误,编译器会打印一条错误消息。是java编译期间对源代码进行语法分析之后,语义分析之前的操作。用树形结构标识源代码,用元素来标识树的节点。我们可以通过AST对代码进行扫描,并进行逻辑写入,也就是通俗的修改代码。
java编译
java编译主要是以下三个阶段
java源文件->语法
java语法分析->生成AST(树形图)->语义分析->编译字节码
生成二进制文件
java语法分析
语法分析主要是通过分析源文件中的字符,将其转化为一个规范的token,包含以下三种类型
关键字 public private static,final等
自定义名称 包名,类名,方法变量名等
运算符号 +-*/&&||等
涉及库以及类
JavaParser 提供用于生成AST的完整API com.github.javaparser
StaticJavaParser 提供快速生成AST的简单API com.github.javaparser.StaticJavaParser
CompilationUnit AST的根
Visitor 查找AST特定部分的类
JavaParser
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-symbol-solver-core</artifactId>
<version>3.15.21</version>
</dependency>
CompilationUnit
代表着已经解析完整且语法正确的源代码的java表示,可以看成一个根节点,可以利用这个根节点访问所有的子节点
example
CompilationUnit cu = StaticJavaParser.parse("class e0mlja { int e0m; }");
其中StaticJavaParser能根据下图看出,接收File,Path,code,Reader,Inputstream类型的参数。
(1)CompilationUnit cuFile = StaticJavaParser.parse(new File("C:\Users\yzc\Desktop\untitled2\src\main\java\example.java"));
(2)CompilationUnit cuPath = StaticJavaParser.parse(Paths.get("C:\Users\yzc\Desktop\untitled2\src\main\java\example.java"));
(3)CompilationUnit cuCode = StaticJavaParser.parse("class example {n" +
" public static void main(String[] args) {n" +
" int i=0;n" +
" System.out.println("e0mlja");n" +
" }n" +
"}");
(4)CompilationUnit cuInput = StaticJavaParser.parse(new FileInputStream(new File("C:\Users\yzc\Desktop\untitled2\src\main\java\example.java")));
(5)CompilationUnit cuReader = StaticJavaParser.parse(new InputStreamReader(new BufferedInputStream(new FileInputStream(new File("C:\Users\yzc\Desktop\untitled2\src\main\java\example.java")))));
#### Visitor
可以通过Visitor,来找到我们特定需要修改或者需要寻找的地方。根据下图2可以看各种node部分。当我们需要查找某个节点的时候,只需要在Visitor中重写visit()方法。下图是实例,简要分析一下代码
* 获取需要查找的类的CompilationUnit
* 获取方法并打印出来
* 获取方法的列表并返回
我们对于需要获取或者更改的,通过上图的节点去寻找,这里就举例file和Method。
##### example
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import sun.reflect.generics.visitor.Visitor;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
public class ASTtest {
public static void main(String[] args) throws IOException {
CompilationUnit cuFile = StaticJavaParser.parse(new File("C:\Users\yzc\Desktop\untitled2\src\main\java\example.java"));
CompilationUnit cuPath = StaticJavaParser.parse(Paths.get("C:\Users\yzc\Desktop\untitled2\src\main\java\example.java"));
CompilationUnit cuCode = StaticJavaParser.parse("class example {n" +
" public static void main(String[] args) {n" +
" int i=0;n" +
" System.out.println("e0mlja");n" +
" }n" +
"}");
CompilationUnit cuInput = StaticJavaParser.parse(new FileInputStream(new File("C:\Users\yzc\Desktop\untitled2\src\main\java\example.java")));
CompilationUnit cuReader = StaticJavaParser.parse(new InputStreamReader(new BufferedInputStream(new FileInputStream(new File("C:\Users\yzc\Desktop\untitled2\src\main\java\example.java")))));
VoidVisitorAdapter<?> methodVisitor = new MethodNamePrinter();
methodVisitor.visit(cuFile,null);
List<String> methodNames = new ArrayList<>();
VoidVisitorAdapter<List<String>> methodNameCollector = new methodNameCollector();
methodNameCollector.visit(cuFile,methodNames);
methodNames.forEach(value-> System.out.println("Method Name Collected: " + value));
VoidVisitorAdapter FiledVisit = new FiledPrinter();
FiledVisit.visit(cuFile,null);
}
private static class MethodNamePrinter extends VoidVisitorAdapter<Void> {
@Override
public void visit(MethodDeclaration md, Void arg) {
super.visit(md, arg);
System.out.println("Method Name Printed: " + md.getName());
}
}
private static class methodNameCollector extends VoidVisitorAdapter<List<String>> {
@Override
public void visit(MethodDeclaration md, List<String> arg) {
super.visit(md, arg);
arg.add(md.getNameAsString());
}
}
private static class FiledPrinter extends VoidVisitorAdapter<Void> {
@Override
public void visit(FieldDeclaration md, Void arg) {
super.visit(md, arg);
System.out.println("Field Name Printed: " + md.getVariables());
}
}
}
原文始发于微信公众号(e0m安全屋):codeql前置之AST和污点分析简述
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论