CNVD-C-2019-48814 wls9-async 反序列化分析

0x01 概述

首先,CNVD收录了由中国民生银行股份有限公司报送的Oracle WebLogic wls9-async反序列化远程命令执行漏洞(CNVD-C-2019-48814)。攻击者利用该漏洞,可在未授权的情况下远程执行命令。从相关信息来看。

部分版本WebLogic中默认包含的wls9_async_response包,为WebLogic Server提供异步通讯服务。由于该WAR包在反序列化处理输入信息时存在缺陷,攻击者可以发送精心构造的恶意 HTTP 请求,获得目标服务器的权限,在未授权的情况下远程执行命令。

也就是说漏洞出现在 wls9_async_response.war 这个包里面,来详细看一看。

0x02 wls9_async_response组件情况

首先先看一下 bea_wls9_async_resp/Users/l1nk3r/blog/blog/source/_posts/Weblogic-CVE-2019-2725-通杀payload.mdonse.war!/WEB-INF/web.xml 内容,我们发现这里的所有请求都是交给 weblogic.wsee.async.AsyncResponseBean 方法继续处理。

而在 bea_wls9_async_response.war!/WEB-INF/weblogic-webservices.xml 文件中,这里定义了目前流传的一些poc请求路径。

那这里需要跟进一下 weblogic.wsee.async.AsyncResponseBean 这个方法,这个方法的位置在 /weblogic.jar!/weblogic/wsee/async/AsyncResponseBean.class ,头部import的包如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
import javax.jws.soap.SOAPBinding.Use;
import javax.xml.rpc.JAXRPCException;
import weblogic.jws.WLDeployment;
import weblogic.jws.WLHttpTransport;
import weblogic.jws.WLHttpsTransport;
import weblogic.jws.WLJmsTransport;
import weblogic.wsee.async.AsyncUtil.SavedServiceInfo;
import weblogic.wsee.deploy.VersioningHelper;
import weblogic.wsee.jws.container.Request;
import weblogic.wsee.message.WlMessageContext;
import weblogic.wsee.util.DirectInvokeUtil;
import weblogic.wsee.util.Verbose;
import weblogic.wsee.ws.WsSkel;

这里关注到了几个 soap 相关的包,从poc中可知触发原因应该是由 于soap注入 引起的反序列化漏洞,但是在 AsyncResponseBean 中只有 handleFaulthandleResult 方法。跟进这两个方法确实没有找到反序列化相关的触发点,然后实际上昨晚这里我卡壳了好久,因为看poc很像 CVE-2017-10271(XMLDecoder反序列化漏洞) ,然后早上起来就看到了这篇《CNTA-2019-0014 wls9-async 反序列化 rce 分析 》,给我开阔了一个新思路。

0x03 新思路

在Servlet拦截器下个断点,
位置在weblogic.jar!/weblogic/wsee/server/servlet/BaseWSServlet.class,从前的import包中可知应该与soap有关系。

跟进一下 weblogic.jar!/weblogic/wsee/server/servlet/SoapProcessor.class

继续跟入 getMethod 方法,
跟到了 weblogic.jar!/weblogic/servlet/internal/ServletRequestImpl.class ,

1
2
3
public String getMethod() {
return this.inputHelper.getRequestParser().getMethod();
}

继续跟入 getRequestParser方法,
位置在weblogic.jar!/weblogic/servlet/internal/ServletRequestImpl.class

1
2
3
public HttpRequestParser getRequestParser() {
return this.parser;
}

上面都没啥用,继续step into又来到了 weblogic.wsee.server.servlet.SoapProcessor#process 中。

1
2
3
4
5
6
7
8
public boolean process(HttpServletRequest var1, HttpServletResponse var2, BaseWSServlet var3) throws IOException {
if ("POST".equalsIgnoreCase(var1.getMethod())) {
this.handlePost(var3, var1, var2);
return true;
} else {
return false;
}
}

然后一直跟进,跟进到
weblogic.jar!/weblogic/wsee/ws/dispatch/server/ServerDispatcher.class 的第85行

继续跟进到 weblogic.jar!/weblogic/wsee/ws/dispatch/server/ServerDispatcher.class 的第93行,跟进 handleRequest(weblogic.jar!/weblogic/wsee/handler/HandlerIterator.class) ,我在下面代码处下个断点。

1
Handler var4 = this.handlers.get(this.index)

这里有很多 handler ,最让我感兴趣的是 AsyncResponseHandler

先看一下这个handler,位置在

weblogic.jar!/weblogic/wsee/async/AsyncResponseHandler.class

我们再看看payload的开头部分。

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8" ?>
<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> <wsa:Action/><wsa:RelatesTo/><asy:onAsyncDelivery/>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">

这里如果我们payload中没有设置RelatesTo属性,它就会直接return flase了,随着执行完这个AsyncResponseHandler之后,我们看到一个最开头的weblogic.wsee.async.AsyncResponseBean 。也就说入口找到了,接下来看看为什么会执行命令。

继续单步跟入到了 WorkAreaServerHandler 这个handler之后,我发现了新大陆。

跟进来之后 getHeader 方法获取了soap中的 http://schemas.xmlsoap.org/soap/envelope/ 内容里面的 Header,然后把它送给WorkContextXmlInputAdapter 做初始化处理并且传入 receiveRequest 函数。

所以这里跟进一下 receiveRequest 方法,
位置在wlclient.jar!/weblogic/workarea/WorkContextLocalMap.class中。

继续跟进一下 readEntry
位置在wlclient.jar!/weblogic/workarea/spi/WorkContextEntryImpl.class中。

跟进一下 readUTF
位置在weblogic.jar!/weblogic/wsee/workarea/WorkContextXmlInputAdapter.class,嘿嘿嘿结果出来了。

1
2
3
public String readUTF() throws IOException {
return (String)this.xmlDecoder.readObject();
}

我们看一下poc。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
POST /_async/AsyncResponseService HTTP/1.1
Content-Type: text/xml
DNT: 0x2424242477686f616d69
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Host: testpay.cmbc.com.cn:443
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: close
Content-Length: 402415

<?xml version="1.0" encoding="utf-8"?>
<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>
<wsa:Action>demoAction</wsa:Action>
<wsa:RelatesTo>hello</wsa:RelatesTo>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"><java>
<class>
<string>oracle.toplink.internal.sessions.UnitOfWorkChangeSet</string>
<void>
<array class="byte" length="8927">

poc依赖于weblogic中的:

com.oracle.toplink_1.1.0.0_11-1-1-6-0.jar!/oracle/toplink/internal/sessions/UnitOfWorkChangeSet.class

在这里下一个断点,看一下调用链,其实是两个内容,第一次是进入xmldecoder的readobject调用我们需要的构造方法oracl.toplink.internal.sessions.UnitOfWorkChangeSet

然后进入oracl.toplink.internal.sessions.UnitOfWorkChangeSet,之后进行二次反序列化。

0x03 弹个计算器

不弹计算器都是刷流氓。

0x04 小结

这个漏洞藏的太深了,跟的过程中step into ,step over跟吐了,只能说发现的人太细心了,第一个xmldecoder只是一个入口,进入之后需要找另一条链组合使用,最后才能达到目的。