#1 前言
之前在文章如何高效地挖掘Java反序列化利用链中提到了我是如何高效挖掘利用链的,这其中提到了工具tabby。
目前,tabby开源也有一段时间了,这段时间里有不少小伙伴问我如何在实际环境中更好地使用它?
为此,本文将介绍我是如何利用tabby捡漏XStream CVE-2021-39147 && CVE-2021-39148。
#2 材料准备跟之前那篇文章提到的一样,我们先对JDK生成代码属性图
# 生成图缓存文件
java -Xmx8g -jar build/libs/tabby-1.1.0-RELEASE.jar --isJDKOnly
# 导入Neo4j图数据
java -Xmx8g -jar build/libs/tabby-1.1.0-RELEASE.jar --isSaveOnly
XStream自1.4.16版本的修复之后,有师傅在TSRC提交了CVE-2021-29505(PS: TSRC yyds)
该链跟之前我们在1.4.15版本时候挖的利用链都不太一样,它重新找到了一个新的触发toString的对象。这条链之前应该有师傅分析过了,就不细说了,这里直接贴一下调用链。
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
这里接过toString函数的是com.sun.xml.internal.ws.api.message.Packet,并且最后以com.sun.jndi.rmi.registry.BindingEnumeration#next触发lookup函数
这里红框框的地方ctx被设置为sun.rmi.registry.RegistryImpl_Stub来对外发起RMI连接。
这里另外说一下,其实CVE-2021-29505的利用链可以简化为
sun.rmi.registry.RegistryImpl_Stub#readObject
sun.rmi.server.UnicastRef#readExternal
# trigger rmi connect
有兴趣的小伙伴可以看RMI绕过相关的文章,除了RegistryImpl_Stub,另外还有link
回到主题,通过29505这条链我们可以对外发起RMI连接,并且他是完全绕过16版本的黑名单的。
为此,XStream的官方出了17版本的黑名单补丁,这里来看一下17版本下所有的黑名单
黑名单字符对象
this.denyTypes(new String[] {
"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"
});
黑名单正则
GETTER_SETTER_REFLECTION = Pattern.compile(".*\\$GetterSetterReflection");
PRIVILEGED_GETTER = Pattern.compile(".*\\$PrivilegedGetter");
LAZY_ENUMERATORS = Pattern.compile(".*\\.Lazy(?:Search)?Enumeration.*");
LAZY_ITERATORS = Pattern.compile(".*\\$LazyIterator");
JAXWS_ITERATORS = Pattern.compile(".*\\$ServiceNameIterator");
JAVAFX_OBSERVABLE_LIST__ = Pattern.compile("javafx\\.collections\\.ObservableList\\$.*");
JAVAX_CRYPTO = Pattern.compile("javax\\.crypto\\..*");
JAVA_RMI = Pattern.compile("(?:java|sun)\\.rmi\\..*");
BCEL_CL = Pattern.compile(".*\\.bcel\\..*\\.util\\.ClassLoader");
黑名单继承对象
this.denyTypeHierarchy(InputStream.class);
this.denyTypeHierarchyDynamically("java.nio.channels.Channel");
this.denyTypeHierarchyDynamically("javax.activation.DataSource");
this.denyTypeHierarchyDynamically("javax.sql.rowset.BaseRowSet");
从上面列举的黑名单,我们可以知道17版本的补丁对29505这条链的几个对象进行了黑名单处理。
Pattern.compile("(?:java|sun)\\.rmi\\..*");
==拉黑==> sun.rmi.registry.RegistryImpl_Stub, sun.rmi.server.UnicastRef
Pattern.compile(".*\\.Lazy(?:Search)?Enumeration.*");
==拉黑==> com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl
到这里我们可以发现,对于29505这条利用链,如下部分利用链仍然是可用的
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
如果能找到从findNextCert函数开始的其他端点,我们仍然能对17版本的补丁进行绕过。
答案当然是肯定的,必然存在一些其他的端点,但是人工去挖掘会特别耗时,那么为了高效捡漏,我们当然不能人工去做这件事。这里我们的tabby就要登场了。
#4 高效捡漏这里我们先来分析一下findNextCert函数
从这里看,我们可以利用类属性aliases和keyStore来进行后续利用链的挖掘。
这里以aliases举例,之前29505利用链就是依靠LazySearchEnumerationImpl#nextElement开始找到的rmi的sink函数。那么,我们现在重新找一个新的,就需要满足如下条件:
- 1.对象实现了java.util.Enumeration接口
- 2.从该对象的nextElement函数开始,最终能到达一个触发恶意行为的sink函数
- 3.从nextElement函数开始到sink函数,路径上不能出现前面黑名单涉及的对象
如果能满足上述条件,那么我们就挖到了能绕过17版本补丁的新利用链。接下来的工作就交给tabby来做了。
首先,我们需要根据上述的条件,描述出具体的查询语句。先来限制一下source函数
match (source:Method {NAME:"nextElement"})
<-[:HAS]-(cls:Class)-[:INTERFACE|EXTENDS*]
->(cls1:Class {NAME:"java.util.Enumeration"})
match (source)-[:CALL]->(m1:Method)
source函数为nextElement,并且实现了java.util.Enumeration接口
再来限制sink函数
match (sink:Method {IS_SINK:true, VUL:"JNDI"})
这里就限制当前sink函数最终能达成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","sun.rmi.registry.RegistryImpl_Stub",
"com.sun.jndi.dns.BindingEnumeration","com.sun.jndi.cosnaming.CNBindingEnumeration","com.sun.jndi.toolkit.dir.HierMemDirCtx$FlatBindings","com.sun.jndi.ldap.LdapReferralException"])
return source,path limit 50
这里第9行上的黑名单是后续人工排除后添加的(不可行或构造过于麻烦)。
把上面的3个部分合起来后查询最终能得到如下3条利用链。
分别是一下对象
- 1.com.sun.jndi.ldap.LdapSearchEnumeration 对应 CVE-2021-39147
- 2.com.sun.jndi.ldap.LdapBindingEnumeration 对应 CVE-2021-39145 (CVE-2021-39151用的也是这个)
- 3.com.sun.jndi.toolkit.dir.ContextEnumerator 对应 CVE-2021-39148
他们分别最终将调用sink函数NamingManager.getContext和DirectoryManager.getObjectInstance
这两个sink函数是JNDI处理Reference的函数,通过这两个函数可以在特定JDK版本下载入外部的任意代码,也可以在tomcat下执行el表达式,具体可以看JNDI关于Reference的知识。
#5 补丁修复XStream 1.4.18版本开始将默认开启白名单模式,只允许几个基础类型的进行还原。
但是这里想不明白的是为何将之前的黑名单处理直接删除了,这意味着未来XStream的安全性将依靠开发者对于反序列化风险的认识。
此外,后续版本绕过默认白名单还是存在可能性的,但是估计没办法像现在这样能造成那么大的危害了吧。
#6 总结本文讲述了我是如何通过tabby来捡漏利用链的,这里欢迎师傅们给tabby提pr或issue。
另外,这里还有一件趣事,由于国内过于积极提交利用链,XStream官方无奈之下默认开启白名单的方式来解决后续可能的绕过。嗯,手动狗头。
,