如何高效地捡漏反序列化利用链?

阅读量    74334 | 评论 2

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

#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

 

#3 背景介绍

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.getContextDirectoryManager.getObjectInstance

这两个sink函数是JNDI处理Reference的函数,通过这两个函数可以在特定JDK版本下载入外部的任意代码,也可以在tomcat下执行el表达式,具体可以看JNDI关于Reference的知识。

 

#5 补丁修复

XStream 1.4.18版本开始将默认开启白名单模式,只允许几个基础类型的进行还原。

但是这里想不明白的是为何将之前的黑名单处理直接删除了,这意味着未来XStream的安全性将依靠开发者对于反序列化风险的认识。

此外,后续版本绕过默认白名单还是存在可能性的,但是估计没办法像现在这样能造成那么大的危害了吧。

 

#6 总结

本文讲述了我是如何通过tabby来捡漏利用链的,这里欢迎师傅们给tabby提pr或issue。

另外,这里还有一件趣事,由于国内过于积极提交利用链,XStream官方无奈之下默认开启白名单的方式来解决后续可能的绕过。嗯,手动狗头。

分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多