基于ASM的java自动化代码审计工具 ACAF(Auto Code Audit Framework)
字数 1805 2025-08-29 08:30:36
Java自动化代码审计工具ACAF教学文档
1. ACAF框架概述
ACAF (Auto Code Audit Framework) 是一个基于ASM的Java自动化代码审计工具,主要针对Spring Web项目进行安全审计。与传统的静态代码分析工具不同,ACAF通过模拟JVM执行字节码来实现污点的动态流动分析。
1.1 核心特点
- 基于字节码分析:直接分析.class文件,无需源代码
- 动态污点分析:模拟JVM执行过程跟踪污点传播
- 可扩展性:用户只需提供漏洞示例代码即可扩展检测能力
- 自动化程度高:相比CodeQL等工具使用更简单
1.2 与CodeQL的主要区别
| 特性 | ACAF | CodeQL |
|---|---|---|
| 分析对象 | 字节码(.class文件) | 源代码 |
| 使用复杂度 | 只需提供Java漏洞示例 | 需要编写QL查询语句 |
| 适用范围 | 专注于Spring Web项目 | 支持多种语言和框架 |
| 自动化程度 | 更高 | 需要更多人工参与 |
2. 核心原理
2.1 技术基础
ACAF基于以下关键技术构建:
- ASM库:用于解析和操作Java字节码
- JVM模拟执行:借鉴gadget-inspector的思路,用Java代码模拟JVM执行过程
- 污点分析:跟踪用户可控数据在程序中的传播路径
2.2 整体架构
+-------------------+ +-------------------+ +-------------------+
| VulnTemplate | | VulnDiscovery | | Audit Core |
| SinkVisitor |--->| (特征存储) |--->| (审计执行) |
+-------------------+ +-------------------+ +-------------------+
^ |
| v
+-------------------+ +-------------------+
| 用户提供的Sink示例 | | 审计结果输出 |
+-------------------+ +-------------------+
3. 使用方法
3.1 基本使用步骤
- 定义Sink示例:编写漏洞代码示例
- 配置污点传播:手动链接污点传播路径
- 创建配置类:注册Sink检测器
- 运行审计:启动ACAF进行扫描
3.2 完整示例
3.2.1 定义SSRF漏洞示例
package com.er1cccc.acaf.example.ssrf;
import com.er1cccc.acaf.config.ControllableParam;
import com.er1cccc.acaf.config.PassthroughRegistry;
import com.er1cccc.acaf.config.Sink;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.lang.reflect.Method;
public class SsrfSink4 implements Sink {
private ControllableParam params = new ControllableParam();
public SsrfSink4(){
params.put("url", "http://localhost");
}
@Override
public Object sinkMethod() throws Exception {
OkHttpClient httpClient = new OkHttpClient();
Request request = new Request.Builder()
.url((String)params.getParameter("url"))
.build();
Response response = httpClient.newCall(request).execute();
return null;
}
@Override
public void addPassthrough(PassthroughRegistry passthroughRegistry) {
try {
Class<?> builder = new Request.Builder().getClass();
Method urlMethod = builder.getMethod("url", String.class);
Method buildMethod = builder.getMethod("build");
Class<OkHttpClient> okHttpClientClass = OkHttpClient.class;
Method newCall = okHttpClientClass.getMethod("newCall", Request.class);
Class<?> call = newCall.getReturnType();
Method execute = call.getMethod("execute");
passthroughRegistry.addPassthrough(urlMethod, 1);
passthroughRegistry.addPassthrough(buildMethod, 0);
passthroughRegistry.addPassthrough(newCall, 1);
passthroughRegistry.addPassthrough(execute, 0);
} catch(Exception e){
e.printStackTrace();
}
}
}
3.2.2 创建配置类
package com.er1cccc.acaf.example.ssrf;
import com.er1cccc.acaf.config.*;
public class SSRFConfigurer implements ACAFConfigurer {
@Override
public void addSource(SourceRegistry sourceRegistry) {
}
@Override
public void addSanitize(SanitizeRegistry sanitizeRegistry) {
}
@Override
public void addSink(SinkRegistry sinkRegistry) {
sinkRegistry.addSink(new SsrfSink4());
}
}
3.2.3 启动审计
package com.er1cccc.acaf.example;
import com.er1cccc.acaf.Launcher;
import com.er1cccc.acaf.example.ssrf.SSRFConfigurer;
public class App {
public static void main(String[] args) throws Exception {
Launcher.launch(new SSRFConfigurer(), args);
}
}
4. 关键技术实现
4.1 漏洞特征抽取
特征抽取过程在VulnTemplateSinkVisitor中实现,主要流程:
- 识别可控参数:通过约定的
params.getParameter()方法识别用户可控点 - 标记污点:将可控参数标记为污点(true)
- 跟踪传播:跟踪污点在方法调用间的传播路径
- 记录特征:将污点传播路径记录为特征
关键代码片段
// 在VulnTemplateSinkVisitor.VulnTemplateSinkMethodVisitor中
@Override
public void visitMethodInsn(String owner, String name, String descriptor, boolean isInterface) {
if(isGetParamMethod(owner, name, descriptor)){
super.visitMethodInsn(owner, name, descriptor, isInterface);
super.setStackTaint(true); // 标记污点
} else {
List<Integer> controllableArgIndex = getControllableArgIndex(owner, name, descriptor);
super.visitMethodInsn(owner, name, descriptor, isInterface);
if(!controllableArgIndex.isEmpty()){
// 记录方法调用特征
compositResolver.addResolver(
new VisitMethodInsnInfoResolver(owner, name, descriptor, controllableArgIndex)
);
} else if(super.getStackTaint(0)){
// 记录返回值污点特征
compositResolver.addResolver(
new VisitMethodInsnInfoResolver(owner, name, descriptor, null)
);
}
}
}
4.2 污点传播处理
ACAF通过PassthroughRegistry处理污点传播中的中断问题:
- 问题原因:gadget-inspector的默认污点传播规则不完善
- 解决方案:用户通过
addPassthrough方法手动指定方法返回值与参数的关联关系
参数下标规则
- 对于静态方法:0表示第一个参数
- 对于非静态方法:0表示this,1表示第一个参数
4.3 自动化审计流程
- 收集Controller参数:作为污点源(Source)
- 深度优先搜索:跟踪污点传播路径
- 特征匹配:与从Sink示例中提取的特征进行比对
- 结果判定:完全匹配所有特征则判定为漏洞
审计核心代码
// 在VulnMethodAdapter中
@Override
public void visitMethodInsn(String owner, String name, String descriptor, boolean isInterface) {
List<Integer> controllableArgIndex = getControllableArgIndex(owner, name, descriptor);
super.visitMethodInsn(owner, name, descriptor, isInterface);
if(vulnResolver.resolve(new VisitMethodInsnInfo(owner, name, descriptor, controllableArgIndex))){
// 特征匹配成功,输出调用栈
outputVulnCallStack();
}
}
5. 实际应用案例
5.1 SSRF漏洞检测
ACAF可以检测多种SSRF实现方式,包括:
- OkHttpClient方式
- HttpURLConnection方式
- HttpClient方式等
5.2 检测效果
在测试项目中:
- 正确识别4个真实的SSRF漏洞点
- 对故意添加的fake sink没有误报
- 能够处理Controller中的复杂参数类型
6. 扩展开发指南
6.1 添加新的漏洞检测
- 创建Sink实现类:编写漏洞示例代码
- 定义污点传播规则:实现
addPassthrough方法 - 注册到配置类:在
ACAFConfigurer中添加Sink
6.2 处理复杂场景
- 复杂参数类型:需要扩展污点源处理逻辑
- 误报处理:未来可通过实现SanitizeRegistry添加净化规则
- 多步骤漏洞:确保完整提取所有关键特征点
7. 总结与展望
ACAF提供了一种相对简单但有效的Java自动化代码审计方案,特别适合Spring Web项目的安全检测。其核心优势在于:
- 使用简单:只需提供Java代码示例即可扩展检测能力
- 准确性高:通过动态污点分析减少误报
- 适应性强:直接分析字节码,适用于各种构建环境
未来可能的改进方向:
- 内置更多常见漏洞的检测模板
- 增强对复杂代码逻辑的分析能力
- 支持更多类型的项目结构(如War包等)
- 添加误报消除机制(Sanitize)