1. 程式人生 > >JNDI注入與反序列化學習總結

JNDI注入與反序列化學習總結

0x01.java RMI

RMI(Remote Method Invocation)是專為Java環境設計的遠端方法呼叫機制,遠端伺服器實現具體的Java方法並提供介面,客戶端本地僅需根據介面類的定義,提供相應的引數即可呼叫遠端方法。
RMI依賴的通訊協議為JRMP(Java Remote Message Protocol ,Java 遠端訊息交換協議),該協議為Java定製,要求服務端與客戶端都為Java編寫。
這個協議就像HTTP協議一樣,規定了客戶端和服務端通訊要滿足的規範。在RMI中物件是通過序列化方式進行編碼傳輸的。

 

如上圖所示,在JVM之間通訊時,客戶端要呼叫遠端伺服器上的物件時,並不是直接將遠端物件拷貝到本地,而是通過傳遞一個stub。

其中stub就包含了遠端伺服器的地址和埠等資訊,可以看作是遠端物件的引用,客戶端可以通過呼叫stub中的方法來對遠端物件進行使用,也就是上圖所說的邏輯上的呼叫,而非直接呼叫,即真實的資料是從客戶端到服務端遠端物件的stub(存根)到伺服器的skeleton(骨架)之間的socket通訊。

實際上client並不知道遠端伺服器的通訊地址和埠,但是伺服器物件存根(stub)中有這些資訊,那麼客戶端只要拿到stub,通過呼叫stub上的方法,然後stub再連線到遠端伺服器的具體埠,伺服器執行client所請求的具體方法,再講執行結果返回給stub,stub再將結果返回給client,此時client表面上看起來即為在本地執行了遠端伺服器上指定物件的方法,而這個執行過程對client實際上是透明的。

至於如何獲取stub,常見方法為通過RMI登錄檔(RMIRegistry)來解決這個問題,RMIRegistry也為遠端物件,此時監聽在1099埠,註冊遠端物件需要RMI URL 和一個遠端物件的引用(實際上就是一個路由對應著一個遠端伺服器上類的例項化物件)。而此時客戶端首先可以通過RMI登錄檔查詢到遠端物件的名稱,先獲取stub,然後來呼叫該stub來呼叫遠端物件所屬類中的方法。

比如如下例子:

遠端服務端:

IHello rhello = new HelloImpl();
LocateRegistry.createRegistry(1099);
Naming.bind("rmi://0.0.0.0:1099/hello", rhello);

此時遠端服務端RMI登錄檔監聽1099埠,並設定RMI URL和對應該URL的類物件

客戶端:

Registry registry = LocateRegistry.getRegistry("遠端伺服器地址",1099);
IHello rhello = (IHello) registry.lookup("hello");
rhello.sayHello("test");

此時客戶端訪問遠端伺服器RMI登錄檔,得到該RMI登錄檔的物件,此時再訪問其中URL中的hello,即可以獲得伺服器端繫結到hello的類的物件,此時就可以進行呼叫sayHello方法,整個流程客戶端也就完成了對遠端伺服器上的類的使用。

動態載入類:

這點根據我的理解應該是服務端可以將不同的url與類寫到RMI登錄檔中,當客戶端的jvm想要呼叫某個類時,可以根據服務端傳遞過來的url去遠端下載類對應的class檔案到本地來進行呼叫。

2.JNDI注入

 jndi介紹:

第一部分已經說過我們可以通過url和類寫到rmi登錄檔中,此時客戶端可以通過url來對遠端類進行載入,而jndi為java服務和目錄介面,JNDI提供統一的客戶端API,通過不同的訪問提供者介面,JNDI服務供應介面(SPI)的實現,由管理者將JNDI API對映為特定的命名服務和目錄系統,使得Java應用程式可以和這些命名服務和目錄服務之間進行互動,所以我們可以通過jdni來訪問遠端的url來獲取我們需要的服務,那麼如果服務端將類註冊到RMI登錄檔中,我們即可以通過jndi來對此類進行訪問。每一個物件都有鍵值對,與名字和物件進行繫結,可以通過名字來對物件進行訪問,物件可能儲存在rmi、ldap中。

java可以將物件儲存在naming或者directory服務下,提供了naming reference功能,物件繫結到reference上,儲存在naming或者directory服務下,(rmi,ldap等)。在使用reference的時候,將物件繫結到構造方法中,從而在被呼叫的時候觸發。

舉個JNDI