专栏首页绿盟科技安全情报Weblogic CVE-2019-2725 分析报告

Weblogic CVE-2019-2725 分析报告

背景

这个漏洞最先由某厂商报给某银行,某银行再将该信息报给CNVD,后CNVD通告:“国家信息安全漏洞共享平台(CNVD)收录了由中国民生银行股份有限公司报送的Oracle WebLogic wls9-async反序列化远程命令执行漏洞(CNVD-C-2019-48814)”。Oracle官方也破例了一回,提前发了补丁,但是这个补丁只是针对10.3.6系列的,对于12版本系列还未披露补丁。所以还是请各位谨慎对待,勒索大军跃跃欲试。

分析

某天接到工程线同事反馈的时候,说wls9-async存在远程代码执行漏洞,可能跟xmldecoder相关,因为一年前分析过该漏洞,详情[ Weblogic XMLDecoder RCE分析]( http://xxlegend.com/2017/12/23/Weblogic%20XMLDecoder%20RCE%E5%88%86%E6%9E%90/):当时第一直觉判断不应该是这个地方再出问题,查了一下相关接口,怀疑是SOAPInvokeState.getClonedSOAPMessage 的问题,后来进一步分析把这个地方排除了。一天后另一个同事给了一个非常模糊的poc,也看不到利用链。隐隐约约看到class,void,这就确定了是xmldecoder的问题,于是聚焦于xmldecoder的补丁。仔细一对比WorkContextXmlInputAdapter的validate接口,还真是能被绕过。

private void validate(InputStream is) {
      WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();

      try {
         SAXParser parser = factory.newSAXParser();
         parser.parse(is, new DefaultHandler() {
            private int overallarraylength = 0;

            public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
               if(qName.equalsIgnoreCase("object")) {
                  throw new IllegalStateException("Invalid element qName:object");
               } else if(qName.equalsIgnoreCase("new")) {
                  throw new IllegalStateException("Invalid element qName:new");
               } else if(qName.equalsIgnoreCase("method")) {
                  throw new IllegalStateException("Invalid element qName:method");
               } else {
                  if(qName.equalsIgnoreCase("void")) {
                     for(int attClass = 0; attClass < attributes.getLength(); ++attClass) {
                        if(!"index".equalsIgnoreCase(attributes.getQName(attClass))) {
                           throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(attClass));
                        }
                     }
                  }

                  if(qName.equalsIgnoreCase("array")) {
                     String var9 = attributes.getValue("class");
                     if(var9 != null && !var9.equalsIgnoreCase("byte")) {
                        throw new IllegalStateException("The value of class attribute is not valid for array element.");
                     }

核心问题在判断void标签和array标签的时候不是遇到这两个标签就抛出异常,而是做了一个for循环遍历,当属性为空的就不会进这个遍历循环,也就不会抛出异常,当然就能直接绕过。像网上一大堆使用类似 <voidclass="xxx">的假poc,假分析文章的时候,就感觉安全圈还真是个娱乐圈。此处笑脸。虽然能过了这个验证环节,但还是需要结合xml的知识来完成完整利用,比如父类,比如Soap定义等等。这也就是有些开发人员更容易构造出绕过的PoC。 知道了漏洞点,构造出PoC还是有挺多拦路虎的,我这里简单列列。

前置知识

使用 -Dweblogic.webservice.verbose=*-Dweblogic.wsee.verbose=*第一步打开调试开关。 weblogic 处理SOAP的方式:

有了这个SOAP请求处理框架图和日志,在发送测试请求的时候就能看出整个处理流程。部分日志如下:

|  <WSEE>/_async/AsyncResponseService  |
  --------------------------------------
<WSEE:23>Created<SoapMessageContext.<init>:48>
<WSEE:23>Processing MessageContextInitHandler...  <HandlerIterator.handleRequest:131>
<WSEE:23>Processing ConnectionHandler...  <HandlerIterator.handleRequest:131>
     ** S T A R T   R E Q U E S T **
<WSEE:23>HTTP REQUEST
  POST /_async/AsyncResponseService
............................
<WSEE:23> from Header, got serverName='AdminServer'<ForwardingHandler.handleRequest:321>
<WSEE:23>from LocalServerIdentity.getIdentity() got serverName='AdminServer'<ForwardingHandler.handleRequest:338>
<WSEE:23>Processing SoapFaultHandler...  <HandlerIterator.handleRequest:131>
<WSEE:23>Processing AsyncResponseWsrmWsscHandler...  <HandlerIterator.handleRequest:131>
<WSEE:23>AsyncResponseWsrmWsscHandler.handleRequest<AsyncResponseWsrmWsscHandler.handleRequest:82>
<WSEE:23>Processing InterceptionHandler...  <HandlerIterator.handleRequest:131>
<WSEE:23>Processing VersionRedirectHandler...  <HandlerIterator.handleRequest:131>

...................

从上述日志就可以看出,所有的请求都会经过webservice注册的21个Handler来处理,我们把断点下在HandlerIterator.handleRequest,就能截取每一个handler。这种通用的责任链模式在web容器中还是很普遍的。具体的handler如下

过程分析

第一步,针对于这个_async入口,我们把重点放在AsyncResponseHandler上,通过其handleRequest的代码

public boolean handleRequest(MessageContext var1) {   
if (var1 == null) {        return true;    } 
else if (!(var1 instanceof SOAPMessageContext)) {        return true;    } 
else {        if (verbose) {            Verbose.log("AsyncResponseHandler.handleRequest");        }       
String var2 = (String)var1.getProperty("weblogic.wsee.addressing.RelatesTo");        if (var2 == null) {    
return false;        } else {

可以看出来必须设置RelatesTo属性,不然就直接返回了,不会进入后面反序列化的流程了。 根据 soap ws.addressing的定义ws -address,其格式为

<wsa:MessageID> xs:anyURI </wsa:MessageID>
<wsa:RelatesTo RelationshipType="..."?>xs:anyURI</wsa:RelatesTo>
<wsa:To>xs:anyURI</wsa:To>
<wsa:Action>xs:anyURI</wsa:Action>
<wsa:From>endpoint-reference</wsa:From>
<wsa:ReplyTo>endpoint-reference</wsa:ReplyTo>
<wsa:FaultTo>endpoint-reference</wsa:FaultTo>

所以poc中的第一步就是要加上ws-address的相关字段。

第二步就是在处理这个xml的过程中,必须删除多余的空格和换行,这是由于xmldecoder处理string标签的差异导致的。根据[StringElementHandler]( http://www.docjar.com/docs/api/com/sun/beans/decoder/StringElementHandler.html)的提示,可以看到

<string>description</string>is equivalent to {@code "description"} in Java code. The value of inner element is calculated before adding to the string using String#valueOf(Object) . Note that all characters are used including whitespaces (' ', '\t', '\n', '\r'). So the value of the element
<string><true></string> is not equal to the value of the element
<string>
    <true>
</string>

也就是说紧凑和不紧凑是有本质区别的。不紧凑的话获取的内容是带换行和空格,这是我当时调试的时候死活找不到类的原因,坑了我不少时间。

第三步,过了前面那个坑就是找相应的payload去执行了,我给oracle提交了 com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext,无视jdk版本限制,目前公开的还有 oracle.toplink.internal.sessions.UnitOfWorkChangeSet,这个类就是利用二次反序列化,二次反序列找的对象可包括 org.springframework.transaction.support.AbstractPlatformTransactionManager ,详情可见我以前的 [分析文档]( http://xxlegend.com/2018/10/23/Weblogic%20CVE-2018-3191%E5%88%86%E6%9E%90/),二次反序列还可以包括jdk7u21,rmi等等gadget,其实第一层的入口也还有挺多类,鉴于这个漏洞的严重性和急迫性,这里不做详细阐述,所以在添加规则的时候一定不能依据利用类来添加规则,说不定明天就被绕过了。下面在ProcessBuilder上下一个断点,调用栈如下:

这个漏洞以前跟过,这里就不再详细阐述。详细跟踪过程参考以前的[分析文档]( http://xxlegend.com/2017/12/23/Weblogic%20XMLDecoder%20RCE%E5%88%86%E6%9E%90/)

修复方式

新的补丁将class加入了黑名单

} else if (qName.equalsIgnoreCase("class")) {
               throw new IllegalStateException("Invalid element qName:class");

这种方式对于漏洞研究人员来说还是挺好玩的。 详细的补丁如下:

private void validate(InputStream is) {
   WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
   try {
      SAXParser parser = factory.newSAXParser();
      parser.parse(is, new DefaultHandler() {
         private int overallarraylength = 0;
         public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (qName.equalsIgnoreCase("object")) {
               throw new IllegalStateException("Invalid element qName:object");
            } else if (qName.equalsIgnoreCase("class")) {
               throw new IllegalStateException("Invalid element qName:class");
            } else if (qName.equalsIgnoreCase("new")) {
               throw new IllegalStateException("Invalid element qName:new");
            } else if (qName.equalsIgnoreCase("method")) {
               throw new IllegalStateException("Invalid element qName:method");
            } else {
               if (qName.equalsIgnoreCase("void")) {
                  for(int i = 0; i < attributes.getLength(); ++i) {
                     if (!"index".equalsIgnoreCase(attributes.getQName(i))) {
                        throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(i));
                     }
                  }
               }
               if (qName.equalsIgnoreCase("array")) {
                  String attClass = attributes.getValue("class");
                  if (attClass != null && !attClass.equalsIgnoreCase("byte")) {
                     throw new IllegalStateException("The value of class attribute is not valid for array element.");
                  }
                  String lengthString = attributes.getValue("length");
                  if (lengthString != null) {
                     try {
                        int length = Integer.valueOf(lengthString);
                        if (length >= WorkContextXmlInputAdapter.MAXARRAYLENGTH) {
                           throw new IllegalStateException("Exceed array length limitation");
                        }
                        this.overallarraylength += length;
                        if (this.overallarraylength >= WorkContextXmlInputAdapter.OVERALLMAXARRAYLENGTH) {
                           throw new IllegalStateException("Exceed over all array limitation.");
                        }
                     } catch (NumberFormatException var8) {

本文分享自微信公众号 - 绿盟科技安全预警(nsfocus_secwarning)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【漏洞预警】Chrome PDF文件解析 0day漏洞预警

    2019年2月28日,国外安全公司发现谷歌Chrome浏览器存在0day漏洞,可导致用户使用Chrome打开恶意PDF文件时发生信息泄露。根据监测,已在野外发现...

    绿盟科技安全情报
  • 【威胁通告】Apache Tomcat 文件包含漏洞(CVE-2020-1938)威胁通告

    2月20日,国家信息安全漏洞共享平台(CNVD)发布了Apache Tomcat文件包含漏洞(CNVD-2020-10487/CVE-2020-1938)。该漏...

    绿盟科技安全情报
  • 【风险预警】Absolute公司防盗追踪软件安全风险

    近日,有研究人员发现其计算机主板BIOS中预置了一款由Absolute公司开发的防盗追踪软件Computrace,在计算机启动后,操作系统会静默安装该软件并向境...

    绿盟科技安全情报
  • java 按钮复选框监听事件及恢复保存状态

    葫芦
  • win10 uwp 气泡 WPF 气泡

    假设尖头宽度 10 高度 5 ,那么可以看到第一个点是 (0,5) 第二个点是 (5,0) 第三个点是 (10,5)

    林德熙
  • 傻瓜式的go modules的讲解和代码

    国内关于gomod的文章,哪怕是使用了百度 -csdn,依然全是理论,虽然golang的使用者大多是大神但是也有像我这样的的弱鸡是不是?

    ydymz
  • ASLR在Windows与Linux系统之间的差别

    作者 Taskiller Hi 基友们,我在上篇文章中讨论了Linux平台上NX的特性。我们已经知道一般情况下NX(Windows平台上称其为DEP)和...

    FB客服
  • 编程小白 | 每日一练(158)

    这道理放在编程上也一并受用。在编程方面有着天赋异禀的人毕竟是少数,我们大多数人想要从编程小白进阶到高手,需要经历的是日积月累的学习,那么如何学习呢?当然是每天都...

    闫小林
  • 从用户成为“股东” —— 在 Apache 基金会的2600天

    4月1日,腾讯云大数据及人工智能产品研发的专家研究员堵俊平受邀成为 Apache 软件基金会Member(Apache Software Foundation ...

    腾讯开源
  • 施一公:无论什么学科,最不重要的就是智商(附毕业演讲)

    今天,我采用的标题是我跟 2010 年入学的研究生分享体验时用的标题,“少年壮志不言愁”。这是一首电视剧的主题曲,也是我最喜欢唱的一首歌曲。

    钱塘数据

扫码关注云+社区

领取腾讯云代金券