转载

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

漏洞编号

CVE-2019-2725、CNVD-C-2019-48814

漏洞简介

WebLogic是美国Oracle公司出品的一个Application Server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。

此漏洞存在于 wls-wsat.warbea_wls9_async_response.war 中的多个路由中。攻击者可利用该漏洞在未授权的情况下远程执行命令。

漏洞影响

  • Oracle WebLogic Server10.3.6.0.0
  • Oracle WebLogic Server12.1.3.0.0

环境搭建

  • Windows Server 2012
  • JDK 1.7.0_21
  • Oracle WebLogic Server10.3.6.0.0

前置知识

在漏洞分析之前,我想先说两个和这个漏洞有关的前置知识:SOAP和Context Propagation

SOAP

SOAP全称Simple Object Access Protocol(简单对象访问协议),是一种XML协议,且通常基于HTTP。用于应用之间的通信和数据交换。

SOAP的消息是一个XML文档,它包含以下元素:

  • 将XML文档标识为SOAP消息的Envelope元素,用于封装SOAP中的所有详细信息。
<soapenv:Envelope>
    <!-- omit... -->
</soapenv:Envelope>
  • 包含头信息的Header元素,头信息可以包含被调用应用需要使用的身份验证凭据,也可以包含SOAP消息中需要使用的复杂类型的定义。
<soapenv:Header>
    <!-- omit... -->
<soapenv:Header>
  • 包含调用和响应信息的Body元素,其中含有需要在Web服务和调用应用程序之间发送的实际数据
<soap:Body>
   <GetSomeInfo>
        <SomeInfoId>omit...</SomeInfoId> 
   </GetSomeInfo>
</soap:Body>

这些元素的结构如下图所示:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析 我们把这些元素合在一起,得到下面的例子:
<soapenv:Envelope>
    <soapenv:Header>
        <!-- omit... -->
    <soapenv:Header>
    <soap:Body>
       <GetSomeInfo>
            <SomeInfoId>omit...</SomeInfoId> 
       </GetSomeInfo>
    </soap:Body>
</soapenv:Envelope>

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

更多有关信息可参考 SOAP Web Services Tutorial

Context Propagation

Context propagation,我在这里翻译成上下文传递,它允许开发者将信息与应用程序相关联,在每一个请求中携带上下文信息。上下文传递的常见用例是将信息 从外界传递到应用程序来取代信息整合到应用程序的一部分 ,它的优势是保持应用程序本身干净,无需多余的API。上下文传递也通常被称作Work Areas、Work Contexts、和Application Transactions。

上下文传递经常用于应用程序的诊断和监控、应用程序的事务处理和负载均衡等。

可以使用如下例子创建上下文传递的应用:

服务端:

package examples.workarea;
// omit...
import weblogic.workarea.WorkContextMap;
import weblogic.workarea.WorkContext;
// omit...
@WebService(name="WorkAreaPortType", 
            serviceName="WorkAreaService",
             targetNamespace="http://example.org")
 @WLHttpTransport(contextPath="workarea", 
                 serviceUri="WorkAreaService",
                  portName="WorkAreaPort")
public class WorkAreaImpl {
    public final static String SESSION_ID = "session_id_key";
   @WebMethod()
    public String sayHello(String message) {
    try {
         WorkContextMap map = (WorkContextMap) new InitialContext().lookup("java:comp/WorkContextMap");
         WorkContext localwc = map.get(SESSION_ID);
         System.out.println("local context: " + localwc);
        System.out.println("sayHello: " + message);
         return "Here is the message: '" + message + "'";
        } catch (Throwable t) {
         return "error";
        }
    }
}

客户端:

package examples.workarea.client;
// omit...
import weblogic.workarea.WorkContextMap;
import weblogic.workarea.WorkContext;
import weblogic.workarea.PrimitiveContextFactory;
import weblogic.workarea.PropagationMode;
import weblogic.workarea.PropertyReadOnlyException;
// omit...
public class Main {
   public final static String SESSION_ID= "session_id_key";
   public static void main(String[] args)
    throws ServiceException, RemoteException, NamingException, PropertyReadOnlyException{
      WorkAreaService service = new WorkAreaService_Impl(args[0] + "?WSDL");
      WorkAreaPortType port = service.getWorkAreaPort();
      WorkContextMap map = (WorkContextMap)new InitialContext().lookup("java:comp/WorkContextMap");
      WorkContext stringContext = PrimitiveContextFactory.create("A String Context");
      // Put a string context
      map.put(SESSION_ID, stringContext, PropagationMode.SOAP);
      try {
        String result = null;
        result = port.sayHello("Hi there!");
        System.out.println( "Got result: " + result );
      } catch (RemoteException e) {
        throw e;
      }
  }
}

更多有关信息可参考 Developing Applications With WebLogic Server

漏洞分析

有了上面的前置知识,我们来详细分析Payload传入Weblogic中到底发生了什么。此次漏洞分析我使用了Java反编译工具JD-GUI配合IDEA远程调试。

以路由 /wls-wsat/CoordinatorPortType 为例,我们就从这个路由的定义入手。

反编译 wls-wsat.war 包,查看其中的 WEB-INF/web.xml 文件,可发现 /CoordinatorPortType 路由的定义位置:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

可以看到其对应的 servlet-classweblogic.wsee.wstx.wsat.v10.endpoint.CoordinatorPortTypePortImpl 。跟入其中,发现此接口是SOAP的实现。

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

其实现的接口如下图:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

这样我们确认了 /wls-wsat/CoordinatorPortType 是SOAP Web Service

然后使用IDEA运行远程调试,将断点放在 WLSServletAdapter.class 128行, handle() 方法,执行Payload:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

可发现请求已经到达其 HttpServletResponse 参数中。

方法调用了它的父类的 handle() 方法,父类的 handle() 方法又继续调用父类 handle() 方法,调用堆栈如下:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

来到 HttpAdapter.class 152行的 handle() 方法,

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

这里主要代码为从队列中取出了一个对象并将其强制转换成 HttpAdapter.HttpToolkit

跟入队列的 take() 方法,因为队列为空,调用了 this.create()

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

Adapter.class 中可看到 create() 函数调用了 Adapter.this.createToolkit()

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

最终在 Toolkit 类中创建了 codechead

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

我们来看下 codechead 分别是什么:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

这里留意 codechead 中的 tube ,稍后会用到。

然后回到 HttpAdapter.class ,调用 tk.handle(connection) ,随后使用刚才的 codec 解码器解码数据包,其调用了 xmlSoapCodec.decode() 方法:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

然后在 StreamSOAPCodec.class 中解码SOAP XML:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

解析的流程忽略不说,再次回到 HttpAdapter.class 的535行:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

此处用到了刚才的 head ,调用其 process() 方法。

根据上面的分析,我们知道了 this.headWSEndpointImpl.class 的对象,其中包含一个成员变量 tubetubeWseeServerTube ,在 WSEndpointImpl.class 的299行创建了一个纤程,然后在303行调用纤程的 runSync() 方法,将 this.tube 传入。纤程中的 runSync() 这样做是为了以同步的方式实现纤程间的异步调用。参数 this.tube 定义了在纤程中要执行的操作,在这里即 WseeServerTube

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

我们跳过一些与纤程相关的操作,来到 WorkContextTube 的子类 WorkContextServerTube.class ,这个类可以看出是用来处理上下文传递(Context Propagation)信息的,来到43行的 readHeaderOld ,顾名思义,是用来读取SOAP中的Header的。

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析 跟入方法,发现此时 var4 正是真正要执行的Payload:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

到112行new了一个 WorkContextXmlInputAdapter 类的对象,补丁就在这个类中。113行调用 this.receive() ,继续跟入:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

WorkContextLocalMap.class 中165行, receiveRequest() 方法,将输入的上下文作为参数调用了 WorkContextEntryImpl.readEntry() 方法, readEntry() 方法又调用 WorkContextXmlInputAdapter.classreadUTF()readUTF() 调用了 this.xmlDecoder.readObject() ,完成了第一次反序列化。

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析 CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

反序列化后反射出Payload中的类的实例,具体反射过程不再跟踪,可以关注 Statement.class 中的 invokeInternal() 。反射后,调用其中的 readObject() ,完成第二次反序列化,触发命令执行:

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

调用过程

此漏洞经历了两次反序列化,整个调用堆栈非常长。类似漏洞的调用堆栈网上已经有很多,可参考 Weblogic XMLDecoder RCE分析 。

在这里我觉得画一个简单的流程图去描述其调用过程会更清晰一些。

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

补丁分析

刚刚在漏洞分析中已经提到了补丁所在位置(WorkContextXmlInputAdapter.class),这个漏洞根本上也是补丁的绕过。我们用刚刚发布的4月份CPU和针对这个漏洞的新的补丁包作对比,发现只多了一个判断分支(左边为4月份CPU中已存在的补丁,右边为新的补丁):

CVE-2019-2725 Weblogic多个组件反序列化远程代码执行漏洞分析

从漏洞上看,在调用 WorkContextXmlInputAdapter 类构造方法时,调用 validate() 方法来校验XML中的每一个标签名。如果包含可能被恶意利用的标签名则抛出错误。是黑名单修补的方式。

修复建议

  • Oracle官方已推出安全更新,请参考以下官方安全通告下载并安装最新补丁:

https://www.oracle.com/technetwork/security-advisory/alert-cve-2019-2725-5466295.html

  • 如果明确不使用wls-wsat.war和bea_wls9_async_response.war,建议删除并重启WebLogic。
  • 通过访问策略控制禁止 /_async/*/wls-wsat/* 路径的URL访问。

参考

  • http://www.cnvd.org.cn/webinfo/show/4989
  • https://www.oracle.com/technetwork/security-advisory/alert-cve-2019-2725-5466295.html
  • https://www.guru99.com/soap-simple-object-access-protocol.html
  • https://docs.oracle.com/cd/E13222_01/wls/docs100/programming/context.html#wp1058673
原文  http://x3fwy.bitcron.com/post/weblogic-rce-wechat
正文到此结束
Loading...