java反序列化利用链自动挖掘工具gadgetinspector源码浅析
字数 1804 2025-08-25 22:58:55

Java反序列化利用链自动挖掘工具GadgetInspector源码深度解析

0x01 工具概述

GadgetInspector是一款自动化挖掘Java反序列化利用链的工具,它通过静态分析技术自动发现从反序列化入口(source)到危险调用点(slink)的完整执行路径。该工具解决了人工分析反序列化利用链耗时耗力的问题,采用了污点分析、调用图构建等关键技术。

主要功能特点

  • 自动化发现Java反序列化利用链
  • 支持多种反序列化方式(原生Java序列化、Jackson等)
  • 采用静态分析方法,无需实际执行代码
  • 实现参数污染传播分析
  • 支持多态方法调用分析

0x02 核心架构与流程

GadgetInspector的分析流程分为五个主要阶段:

  1. MethodDiscovery:收集类、方法数据及继承关系
  2. PassthroughDiscovery:分析参数对返回值的污染关系
  3. CallGraphDiscovery:构建方法调用图及参数关联
  4. SourceDiscovery:识别反序列化入口方法
  5. GadgetChainDiscovery:整合数据发现完整利用链

0x03 MethodDiscovery阶段详解

3.1 功能目标

收集三类核心数据:

  • 类信息(类名、父类、接口、字段等)
  • 方法信息(类名、方法名、描述符、静态标志等)
  • 类继承关系(父子类、接口实现关系)

3.2 实现原理

使用ASM框架的ClassVisitor和MethodVisitor遍历所有类文件:

public void discover(final ClassResourceEnumerator classResourceEnumerator) throws Exception {
    for (ClassResourceEnumerator.ClassResource classResource : classResourceEnumerator.getAllClasses()) {
        ClassReader cr = new ClassReader(in);
        cr.accept(new MethodDiscoveryClassVisitor(), ClassReader.EXPAND_FRAMES);
    }
}

3.3 数据存储格式

  1. classes.dat

    类名 父类 接口列表 是否接口 字段信息
    

    字段信息格式:字段名!访问修饰符!类型

  2. methods.dat

    类名 方法名 方法描述符 是否静态方法
    
  3. inheritanceMap.dat

    类名 父类/接口1 父类/接口2 ...
    

0x04 PassthroughDiscovery阶段详解

4.1 功能目标

分析方法的参数是否会影响返回值,建立参数索引与返回值的污染关系。

4.2 关键技术:污点分析

通过模拟JVM栈帧操作,跟踪参数数据流:

  1. 本地变量表(Local Variables):存储方法参数和局部变量
  2. 操作数栈(Operand Stack):跟踪方法调用时的参数传递

4.3 核心实现

使用ASM MethodVisitor实现字节码级别的数据流分析:

@Override
public void visitVarInsn(int opcode, int var) {
    // 处理局部变量加载/存储指令
    switch(opcode) {
        case Opcodes.ALOAD:
            push(savedVariableState.localVars.get(var));
            break;
        case Opcodes.ASTORE:
            saved0 = pop();
            savedVariableState.localVars.set(var, saved0);
            break;
        // ...其他指令处理
    }
}

@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
    // 处理方法调用指令,分析参数污染传播
    Set<Integer> passthrough = passthroughDataflow.get(methodRef);
    if (passthrough != null) {
        for (Integer passthroughDataflowArg : passthrough) {
            resultTaint.addAll(argTaint.get(passthroughDataflowArg));
        }
    }
}

4.4 逆拓扑排序

为确保分析方法时其调用方法已被分析,采用DFS逆拓扑排序:

private static void dfsTsort(Map<MethodReference.Handle, Set<MethodReference.Handle>> outgoingReferences,
                           List<MethodReference.Handle> sortedMethods, 
                           Set<MethodReference.Handle> visitedNodes,
                           Set<MethodReference.Handle> stack, 
                           MethodReference.Handle node) {
    // DFS实现逆拓扑排序
    if (visitedNodes.contains(node)) return;
    stack.add(node);
    for (MethodReference.Handle child : outgoingRefs) {
        dfsTsort(outgoingReferences, sortedMethods, visitedNodes, stack, child);
    }
    stack.remove(node);
    visitedNodes.add(node);
    sortedMethods.add(node);
}

4.5 数据存储格式

passthrough.dat

类名 方法名 方法描述符 污染参数索引列表

0x05 CallGraphDiscovery阶段详解

5.1 功能目标

建立方法调用图,记录调用者与被调用者之间的参数传递关系。

5.2 关键数据结构

class GraphCall {
    MethodReference.Handle callerMethod;  // 调用者方法
    MethodReference.Handle targetMethod;  // 被调用方法
    int callerArgIndex;                   // 调用者参数索引
    String callerArgPath;                 // 调用者字段路径(如"arg0.field")
    int targetArgIndex;                   // 被调用者参数索引
}

5.3 数据存储格式

callgraph.dat

调用者类名 调用者方法名 调用者方法描述符 
被调用者类名 被调用者方法名 被调用者方法描述符 
调用者参数索引 调用者字段路径 被调用者参数索引

0x06 SourceDiscovery阶段详解

6.1 功能目标

识别反序列化过程中会被自动调用的方法(入口点)。

6.2 不同序列化方式的入口

  1. Java原生序列化

    • readObject()
    • readResolve()
    • readExternal()
  2. Jackson反序列化

    • 无参构造方法
    • setter方法(setXxx)
    • getter方法(getXxx)

6.3 实现示例(Jackson)

public class JacksonSourceDiscovery extends SourceDiscovery {
    public void discover(...) {
        for (MethodReference.Handle method : methodMap.keySet()) {
            if (serializableDecider.apply(method.getClassReference())) {
                if (method.getName().equals("<init>") && method.getDesc().equals("()V")) {
                    addDiscoveredSource(new Source(method, 0));
                }
                if (method.getName().startsWith("get") && method.getDesc().startsWith("()")) {
                    addDiscoveredSource(new Source(method, 0));
                }
                // ...其他条件
            }
        }
    }
}

6.4 数据存储格式

sources.dat

类名 方法名 方法描述符 污染参数索引

0x07 GadgetChainDiscovery阶段详解

7.1 功能目标

整合前几个阶段的数据,发现从source到slink的完整利用链。

7.2 关键步骤

  1. 加载方法实现关系

    Map<MethodReference.Handle, Set<MethodReference.Handle>> methodImplMap = 
        InheritanceDeriver.getAllMethodImplementations(inheritanceMap, methodMap);
    
  2. 构建调用图

    Map<MethodReference.Handle, Set<GraphCall>> graphCallMap = new HashMap<>();
    for (GraphCall graphCall : loadData("callgraph.dat")) {
        graphCallMap.computeIfAbsent(graphCall.getCallerMethod(), k -> new HashSet<>())
                   .add(graphCall);
    }
    
  3. 链式搜索算法

    while (!methodsToExplore.isEmpty()) {
        GadgetChain chain = methodsToExplore.pop();
        GadgetChainLink lastLink = chain.links.get(chain.links.size()-1);
    
        for (GraphCall graphCall : graphCallMap.get(lastLink.method)) {
            Set<MethodReference.Handle> allImpls = 
                implementationFinder.getImplementations(graphCall.getTargetMethod());
    
            for (MethodReference.Handle methodImpl : allImpls) {
                GadgetChainLink newLink = new GadgetChainLink(methodImpl, graphCall.getTargetArgIndex());
                if (isSink(methodImpl, graphCall.getTargetArgIndex(), inheritanceMap)) {
                    discoveredGadgets.add(new GadgetChain(chain, newLink));
                } else {
                    methodsToExplore.add(new GadgetChain(chain, newLink));
                }
            }
        }
    }
    

7.3 Sink点判断

工具内置的危险方法(slink)包括:

private boolean isSink(MethodReference.Handle method, int argIndex, InheritanceMap inheritanceMap) {
    // Runtime.exec()
    if (method.getClassReference().getName().equals("java/lang/Runtime") 
            && method.getName().equals("exec")) {
        return true;
    }
    
    // Method.invoke()
    if (method.getClassReference().getName().equals("java/lang/reflect/Method")
            && method.getName().equals("invoke") && argIndex == 0) {
        return true;
    }
    
    // URL.openStream()
    if (method.getClassReference().getName().equals("java/net/URL") 
            && method.getName().equals("openStream")) {
        return true;
    }
    
    // ...其他危险方法
}

0x08 扩展与定制

8.1 支持新的序列化方式

通过实现GIConfig接口:

public interface GIConfig {
    String getName();
    SerializableDecider getSerializableDecider(...);
    ImplementationFinder getImplementationFinder(...);
    SourceDiscovery getSourceDiscovery();
}

8.2 自定义Source发现

继承SourceDiscovery类并实现discover方法:

public class CustomSourceDiscovery extends SourceDiscovery {
    @Override
    public void discover(...) {
        // 实现自定义的入口点发现逻辑
    }
}

8.3 添加新的Sink点

修改isSink方法,添加新的危险方法判断条件。

0x09 总结

GadgetInspector通过静态分析技术实现了Java反序列化利用链的自动化挖掘,其核心创新点包括:

  1. 污点传播分析:精确跟踪参数对返回值的污染关系
  2. 逆拓扑排序:确保分析方法时其调用方法已被分析
  3. 多态方法处理:考虑接口/父类方法的所有实现
  4. 模块化设计:支持多种反序列化方式的扩展

该工具为Java反序列化漏洞研究提供了强有力的自动化支持,极大提高了漏洞挖掘的效率。

Java反序列化利用链自动挖掘工具GadgetInspector源码深度解析 0x01 工具概述 GadgetInspector是一款自动化挖掘Java反序列化利用链的工具,它通过静态分析技术自动发现从反序列化入口(source)到危险调用点(slink)的完整执行路径。该工具解决了人工分析反序列化利用链耗时耗力的问题,采用了污点分析、调用图构建等关键技术。 主要功能特点 自动化发现Java反序列化利用链 支持多种反序列化方式(原生Java序列化、Jackson等) 采用静态分析方法,无需实际执行代码 实现参数污染传播分析 支持多态方法调用分析 0x02 核心架构与流程 GadgetInspector的分析流程分为五个主要阶段: MethodDiscovery :收集类、方法数据及继承关系 PassthroughDiscovery :分析参数对返回值的污染关系 CallGraphDiscovery :构建方法调用图及参数关联 SourceDiscovery :识别反序列化入口方法 GadgetChainDiscovery :整合数据发现完整利用链 0x03 MethodDiscovery阶段详解 3.1 功能目标 收集三类核心数据: 类信息(类名、父类、接口、字段等) 方法信息(类名、方法名、描述符、静态标志等) 类继承关系(父子类、接口实现关系) 3.2 实现原理 使用ASM框架的ClassVisitor和MethodVisitor遍历所有类文件: 3.3 数据存储格式 classes.dat : 字段信息格式: 字段名!访问修饰符!类型 methods.dat : inheritanceMap.dat : 0x04 PassthroughDiscovery阶段详解 4.1 功能目标 分析方法的参数是否会影响返回值,建立参数索引与返回值的污染关系。 4.2 关键技术:污点分析 通过模拟JVM栈帧操作,跟踪参数数据流: 本地变量表(Local Variables) :存储方法参数和局部变量 操作数栈(Operand Stack) :跟踪方法调用时的参数传递 4.3 核心实现 使用ASM MethodVisitor实现字节码级别的数据流分析: 4.4 逆拓扑排序 为确保分析方法时其调用方法已被分析,采用DFS逆拓扑排序: 4.5 数据存储格式 passthrough.dat : 0x05 CallGraphDiscovery阶段详解 5.1 功能目标 建立方法调用图,记录调用者与被调用者之间的参数传递关系。 5.2 关键数据结构 5.3 数据存储格式 callgraph.dat : 0x06 SourceDiscovery阶段详解 6.1 功能目标 识别反序列化过程中会被自动调用的方法(入口点)。 6.2 不同序列化方式的入口 Java原生序列化 : readObject() readResolve() readExternal() Jackson反序列化 : 无参构造方法 setter方法(setXxx) getter方法(getXxx) 6.3 实现示例(Jackson) 6.4 数据存储格式 sources.dat : 0x07 GadgetChainDiscovery阶段详解 7.1 功能目标 整合前几个阶段的数据,发现从source到slink的完整利用链。 7.2 关键步骤 加载方法实现关系 : 构建调用图 : 链式搜索算法 : 7.3 Sink点判断 工具内置的危险方法(slink)包括: 0x08 扩展与定制 8.1 支持新的序列化方式 通过实现GIConfig接口: 8.2 自定义Source发现 继承SourceDiscovery类并实现discover方法: 8.3 添加新的Sink点 修改isSink方法,添加新的危险方法判断条件。 0x09 总结 GadgetInspector通过静态分析技术实现了Java反序列化利用链的自动化挖掘,其核心创新点包括: 污点传播分析 :精确跟踪参数对返回值的污染关系 逆拓扑排序 :确保分析方法时其调用方法已被分析 多态方法处理 :考虑接口/父类方法的所有实现 模块化设计 :支持多种反序列化方式的扩展 该工具为Java反序列化漏洞研究提供了强有力的自动化支持,极大提高了漏洞挖掘的效率。