专栏首页FreeBufWeblogic漏洞反序列化(CVE-2019-2725)分析

Weblogic漏洞反序列化(CVE-2019-2725)分析

一、漏洞描述

4月17日,国家信息安全漏洞共享平台(CNVD)公开了Weblogic反序列化远程代码执行漏洞(CNVD-C-2019-48814)。由于在反序列化处理输入信息的过程中存在缺陷,未经授权的攻击者可以发送精心构造的恶意 HTTP 请求,利用该漏洞获取服务器权限,实现远程代码执行。目前,POC已在野外公开(见参考链接)。官方紧急补丁(CVE-2019-2725)已于4月26日发布,请受影响主机及时修复漏洞。

受影响版本

Oracle WebLogic Server 10.* Oracle WebLogic Server 12.1.3

影响组件:

bea_wls9_async_response.war wsat.war

二、漏洞分析

根据国家信息安全漏洞共享平台(CNVD)漏洞公告,此漏洞存在于异步通讯服务,可通过访问路径/_async/AsyncResponseService,判断不安全组件是否开启。wls9_async_response.war包中的类由于使用注解方法调用了Weblogic原生处理Web服务的类,因此会受该漏洞影响:

为更好的理解漏洞成因,通过IDEA对WebLogic服务器远程动态调试(因为需要跟进原生类中的方法,需要在IDEA中指定WebLogic安装目录中的JDK文件夹),在ProcessBuilder类中打下断点,关键的调用栈过程如下所示:

调用栈非常深,下面解释一下几个关键的部分。首先是继承自HttpServlet的BaseWSServlet类,其中的service方法主要用于处理HTTP请求及其响应,通过HTTP协议发送的请求包封装在HttpServletRequest类的实例化对象var1中:

调用BaseWSServlet中定义的内部类AuthorizedInvoke的run()方法完成传入HTTP对象的权限验证过程:

若校验成功,则进入到SoapProcessor类的process方法中,通过调用HttpServletRequest类实例化对象var1的getMethod()方法获取HTTP请求类型,若为POST方法,则继续处理请求:

HTTP请求发送至SoapProcessor类的handlePost方法中:

private void handlePost(BaseWSServlet var1, HttpServletRequest var2, HttpServletResponse var3) throws IOException {
    assert var1.getPort() != null;
    WsPort var4 = var1.getPort();
    String var5 = var4.getWsdlPort().getBinding().getBindingType();
    HttpServerTransport var6 = new HttpServerTransport(var2, var3);
    WsSkel var7 = (WsSkel)var4.getEndpoint();
    try {
        Connection var8 = ConnectionFactory.instance().createServerConnection(var6, var5);
        var7.invoke(var8, var4);
    } catch (ConnectionException var9) {
        this.sendError(var3, var9, "Failed to create connection");
    } catch (Throwable var10) {
        this.sendError(var3, var10, "Unknown error");
    }
}

为方便后续分析工作进行,在此先简单介绍一下SOAP协议内容及格式:SOAP(中文称之为简单对象访问协议),用于在WEB上交换结构化和固化的信息,是Web Service三要素之一,可以和现存的许多因特网协议和格式结合使用。下图展示SOAP消息封装的标准格式:

BaseWSServlet类实例化对象var1封装了基于HTTP协议的SOAP消息:

调用var1对象中定义的getPort()方法解析SOAP消息中的根元素Envelope(可把 XML 文档定义为 SOAP 消息),获取所调用服务的端口信息:

通过var4对象的getWsdlPort().getBinding().getBindingType()方法获取当前SOAP协议规范版本信息:

并将HttpServletRequest类的var2对象及HttpServletResponse类的对象var3传入到HttpServerTransport类构造函数中初始化实例对象var6统一处理后续HTTP请求及响应。

继续调用var4对象中getEndpoint()方法完成对SOAP消息中根元素Envelope解析并读取与其相关联的xmlns:soap命名空间,其后分别完成对SOAP Header元素和Body元素解析工作:

跟进WsSkel类中定义的invoke()方法,其中完成了ServerDispatcher类实例化过程,并调用setWsPort()方法指定服务请求地址,进入调试器查看WsPort对象var2的属性值,发现底层依靠HashMap数据结构保存请求服务的Address和URI,其中当前请求http://:7001/_async/AsyncResponseService服务:

在调试器中查看ServerDispatcher对象var5属性值,发现methodName属性中赋值了onAsyncDelivery方法名,在调用dispatch()方法时将调用上述服务中定义的该方法:

WorkAreaServerHandler类中的handleRequest()方法用于处理访问请求,通过WlMessageContext对象var2获取传入的MessageContext,调用var2对象的getHeaders()方法获取传入SOAP消息的Header元素,并最终将该元素传递到WorkAreaHeader对象var4中,可以在调试器中清晰看到元素内容的赋值:

新建WorkContextMapInterceptor对象var5,在其receiveRequest()方法中读入经WorkContextXmlInputAdapter适配器构造函数转换过后的var4对象字节数组输出流,经内部getMap() -> receiveRequest() -> readEntry() 方法处理后,将上述Content字段传入至WorkContextXmlInputAdapter类的readUTF()方法中:

readUTF()方法中调用WorkContextXmlInputAdapter类私有成员变量xmlDecoder的readObject()方法读取字节数组,经内部SAXParser类链式调用一系列解析器的parse()方法后,最终在com.sun.beans.ObjectHandler类定义的endElement()方法中完成XML文档元素解析过程,获取了有效类名oracle.toplink.internal.sessions.UnitOfWorkChangeSet:

在Security机制完成对类名权限校验后,利用Java反射机制,通过元类定义的newInstance()方法实现上述类的实例化过程:

同样通过反射包中的Constructor类调用构造器方法传入字节数组,为上述实例对象赋初值:

UnitOfWorkChangeSet对象完成初始化过程后,使用ByteArrayInputStream对象接收经构造函数传入的字节数组,再将ByteArrayInputStream对象byteIn转换为ObjectInputStream对象objectIn,并直接调用了objectIn对象的readObject()方法。由于WebLogic安装包中默认SDK为1.6版本,在JDK版本<=JDK7u21前提下存在Java原生类反序列化漏洞,使用ysoserial工具生成恶意序列化对象(以计算器程序为例),可在调试器中查看到当前所传入的序列化对象:

经readObject()方法反序列化恶意对象后通过在ProcessBuilder类的start()方法断点处查看command属性,发现成功传入“calc”字符串:

到此就完成了漏洞利用的全过程,下图演示了漏洞利用效果,通过反序列化漏洞成功运行了计算器程序:

三、补丁绕过

下面来分析下本次漏洞和以前所公布的CVE-2017-3506和CVE-2017-10271之间的关系,依旧从补丁diff着手,上述两个补丁都是在weblogic/wsee/workarea/WorkContextXmlInputAdapter.java中添加了validate方法。首先来看CVE-2017-3506补丁文件,其实现方法简单来说就是在调用startElement方法解析XML的过程中,如果解析到Element字段值为Object就抛出异常:

private void validate(InputStream is) {      WebLogicSAXParserFactoryfactory = new WebLogicSAXParserFactory();      try {         SAXParser parser =factory.newSAXParser();         parser.parse(is, newDefaultHandler() {            public void startElement(String uri, StringlocalName, String qName, Attributes attributes) throws SAXException {               if(qName.equalsIgnoreCase("object")) {                  throw newIllegalStateException("Invalid context type: object");               }            }         });      } catch(ParserConfigurationException var5) {         throw newIllegalStateException("Parser Exception", var5);      } catch (SAXExceptionvar6) {         throw newIllegalStateException("Parser Exception", var6);      } catch (IOExceptionvar7) {         throw newIllegalStateException("Parser Exception", var7);      }   }

但上述这类采用黑名单的防护措施很快就被如下POC轻松绕过,因为其中不包含任何Object元素,但经XMLDecoder解析后依旧造成了远程代码执行:

<java version="1.4.0" class="java.beans.XMLDecoder">    <new class="java.lang.ProcessBuilder">        <string>calc</string><method name="start" />    </new></java>

针对如上所示一系列bypass CVE-2017-3506补丁限制的POC的产生,官方在同年十月份发布了CVE-2017-10271补丁文件。和上述不同点在于本次更新中官方将object、new、method关键字继续加入到黑名单中,一旦解析XML元素过程中匹配到上述任意一个关键字就立即抛出运行时异常。但是针对void和array这两个元素是有选择性的抛异常,其中当解析到void元素后,还会进一步解析该元素中的属性名,若没有匹配上index关键字才会抛出异常。而针对array元素而言,在解析到该元素属性名匹配class关键字的前提下,还会解析该属性值,若没有匹配上byte关键字,才会抛出运行时异常:

public void startElement(String uri, String localName, String qName, Attributesattributes) throws SAXException {            if(qName.equalsIgnoreCase("object")) {               throw newIllegalStateException("Invalid element qName:object");            } else if(qName.equalsIgnoreCase("new")) {               throw newIllegalStateException("Invalid element qName:new");            } else if(qName.equalsIgnoreCase("method")) {               throw newIllegalStateException("Invalid element qName:method");            } else {               if(qName.equalsIgnoreCase("void")) {                  for(int attClass = 0; attClass < attributes.getLength();++attClass) {                     if(!"index".equalsIgnoreCase(attributes.getQName(attClass))){                        throw newIllegalStateException("Invalid attribute for elementvoid:" + attributes.getQName(attClass));                     }                  }               }               if(qName.equalsIgnoreCase("array")) {                  String var9 =attributes.getValue("class");                  if(var9 != null &&!var9.equalsIgnoreCase("byte")) {                     throw newIllegalStateException("The value of class attribute is notvalid for array element.");                  }

本次反序列化漏洞绕过以往补丁的关键点在于利用了Class元素指定任意类名,因为CVE-2017-10271补丁限制了带method属性的void元素,所以不能调用指定的方法,而只能调用完成类实例化过程的构造方法。在寻找利用链的过程中发现UnitOfWorkChangeSet类构造方法中直接调用了JDK原生类中的readObject()方法,并且其构造方法的接收参数恰好是字节数组,这就满足了上一个补丁中array标签的class属性值必须为byte的要求,再借助带index属性的void元素,完成向字节数组中赋值恶意序列化对象的过程,最终利用JDK 7u21反序列化漏洞造成了远程代码执行。通过巧妙的利用了void、array和Class这三个元素成功的打造了利用链,再次完美的绕过了CVE-2017-10271补丁限制,本次漏洞的发现进一步证明了依靠黑名单机制是一种不可靠的防护措施。

四、临时修复建议

官方目前已发布针对此漏洞的紧急修复补丁,可以采取以下4种方式进行防护。

及时打上官方CVE-2019-2725补丁包

官方已于4月26日公布紧急补丁包,下载地址如下:https://www.oracle.com/technetwork/security-advisory/alert-cve-2019-2725-5466295.html?from=timeline

升级本地JDK版本

因为Weblogic所采用的是其安装文件中默认1.6版本的JDK文件,属于存在反序列化漏洞的JDK版本,因此升级到JDK7u21以上版本可以避免由于Java原生类反序列化漏洞造成的远程代码执行。

配置URL访问控制策略

部署于公网的WebLogic服务器,可通过ACL禁止对/_async/及/wls-wsat/路径的访问。

删除不安全文件

删除wls9_async_response.war与wls-wsat.war文件及相关文件夹,并重启Weblogic服务。具体文件路径如下:

10.3.*版本:

12.1.3版本:

注:wls9_async_response.war及wls-wsat.war属于一级应用包,对其进行移除或更名操作可能造成未知的后果,Oracle官方不建议对其进行此类操作。若在直接删除此包的情况下应用出现问题,将无法得到Oracle产品部门的技术支持。请用户自行进行影响评估,并对此文件进行备份后,再执行此操作。

五、Reference

http://www.cnvd.org.cn/webinfo/show/4999 https://www.oracle.com/technetwork/security-advisory/alert-cve-2019-2725-5466295.html?from=timeline https://devcentral.f5.com/articles/oracle-weblogic-deserialization-remote-code-execution-34185

本文分享自微信公众号 - FreeBuf(freebuf),作者:云影实验室

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

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 开源BUG跟踪平台JIRA目录遍历漏洞分析

    作者 Taskiller 最近,一则新发布的公告报告了一个影响Jira 5.0.11和6.0.3版本的目录遍历漏洞,该漏洞在去年7月份被验证,并在接下来的几个月...

    FB客服
  • 美国邮政服务网站漏洞可暴露6000万用户数据,现已修复

    美国邮政服务系统刚刚修复了一个严重的网站漏洞,该漏洞使得拥有usps.com帐户的任何人都可查看和修改约6000万用户的账户详情。

    FB客服
  • 使用第三方库进行软件开发的安全风险研究

    如今,很多软件由于长期使用第三方库文件,导致了持续的安全问题。而在程序开发设计阶段,开发者又经常忽略了第三方库代码的漏洞审查,甚至有些资源库(repositor...

    FB客服
  • 七情六欲聊运营----如何做更懂用户的产品运营【上篇】

    5月10日,由人人都是产品经理和腾讯大讲堂共同举办的2015中国产品经理大会全国巡回-广州站在华南理工大学举行,本篇是腾讯高级产品经理陈婷婷的《 七情六欲聊运营...

    腾讯大讲堂
  • BigDecimal类型divide报错

    java.lang.ArithmeticException: Non-terminating decimal expansion; no exact repre...

    Java架构师历程
  • Android内存优化(三)避免可控的内存泄漏

    前言 内存泄漏向来都是内存优化的重点,它如同幽灵一般存于我们的应用当中,有时它不会现身,但一旦现身就会让你头疼不已。因此,如何避免、发现和解决内存泄漏就变得尤为...

    用户1269200
  • 熔断器设计模式

    如果大家有印象的话,尤其是夏天,如果家里用电负载过大,比如开了很多家用电器,就会”自动跳闸”,此时电路就会断开。在以前更古老的一种方式是”保险丝”,当负载过大,...

    逸鹏
  • [译] 通过安全浏览保护 WebView

    Android 开发者
  • 执行truncate引发ORA-02266的问题分析

    墨墨导读:将测试数据库的数据清空,其中涉及主子表的关系,执行truncate产生的ORA-02266问题处理过程。

    数据和云
  • Django:web框架的学习(2)

    谢伟

扫码关注云+社区

领取腾讯云代金券