JAVA_RMI
RMI
RMI全称是Remote Method Invocation,远程⽅法调⽤。使用 RMI 技术可以使一个 JVM 中的对象,调用另一个 JVM 中的对象方法并获取调用结果。这里的另一个 JVM 可以在同一台计算机也可以是远程计算机。
RMI主要由三部分组成
1 2 3
| Server: 提供远程的对象 Client: 调用远程的对象 Registry: 一个注册表,存放着远程对象的位置(ip、端口、标识符)
|
Java本身对RMI规范的实现默认使用的是JRMP协议。
1
| JRMP:Java Remote Message Protocol ,Java 远程消息交换协议。这是运行在Java RMI之下、TCP/IP之上的线路层协议。该协议要求服务端与客户端都为Java编写,就像HTTP协议一样,规定了客户端和服务端通信要满足的规范
|
而在Weblogic中对RMI规范的实现使用T3协议。
1
| WebLogic T3协议(Two-Tier TCP/IP Protocol)是Oracle WebLogic Server中的一种专有协议,它建立在TCP/IP协议之上,用于在客户端和服务器之间进行通信。T3协议是WebLogic Server的默认通信协议,主要用于处理Java客户端和WebLogic Server之间的交互。
|
Server&Registry
Server需要构建一个可以被传输的类,一个可以被远程访问的类,同时这个对象要被注册到RMI中开放给客户端使用
Registry需要将我们的类和端口放入到注册表里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package org.example.java_RMI;
import java.io.IOException; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.server.UnicastRemoteObject;
public class server { public interface RemoteInterface extends java.rmi.Remote { public String exp() throws IOException; }
public class example extends UnicastRemoteObject implements RemoteInterface{
protected example() throws RemoteException { }
@Override public String exp() throws IOException { Runtime.getRuntime().exec("calc"); return null; } } private void start() throws RemoteException, MalformedURLException { example exp = new example(); LocateRegistry.createRegistry(1145); Naming.rebind("rmi://127.0.0.1:1145/exp",exp); }
public static void main(String[] args) throws MalformedURLException, RemoteException { new server().start(); System.out.println("sever is running"); } }
|
我们在接口RemoteInterface里面定义了一个可以被远程调用的函数exp()
然后example类实现了这个接口。最后使用主类创建了这个server
Client
需要引入可以远程访问和需要传输的类,通过端口和绑定了sever的地址,就可以发起调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package org.example.java_RMI;
import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry;
public class Client { public static void main(String[] args) throws MalformedURLException, RemoteException { try { server.RemoteInterface myclass = (server.RemoteInterface) Naming.lookup("rmi://127.0.0.1:1145/exp"); String response = myclass.exp(); System.out.println(response); } catch (Exception e) { e.printStackTrace(); } } }
|
可以看到成功获取这个对象,并且调用了方法
过程
首先客户端(client)链接注册端(Registry),然后在里面寻找相关的类exp,寻找到之后,Registry返还一个序列化的数据。然后这个时候客户端会对这个对象进行反序列化,发现是远程对象,然后发起链接,才链接的过程中执行远程方法调用
远程方法实际上只在Server上调用,在本地不调用
危险方法探测
如果我们可以访问目标的RMI Registry ,我们可以查看上面存在那些可能的危险方法
java虽然存在相关的限制,只有来源地址是localhost的时候,才能调用rebind、 bind、unbind等方法。,不过依然可以使用list方法和lookup探测危险方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package org.example.java_RMI;
import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry;
public class Client { public static void main(String[] args) throws MalformedURLException, RemoteException { try {
String[] remoteObjects = Naming.list("rmi://127.0.0.1:1145/"); System.out.println("存在的方法是"); for (String object : remoteObjects) { System.out.println(object); }
} catch (Exception e) { e.printStackTrace(); } } }
|
codebase完成rce
在RMI 的过程中,客户端和服务端会传递一些序列化之后的数据,对象在反序列化的时候,会去寻找对应的类,如果寻找失败,就回去寻找classpath下的类,如果还是寻找失败,就会加载codebase,去加载一个远程的类,如果能控制这个codebase,就能加载恶意类
利用条件
1 2 3 4 5
| java7 < 7u21 java6 < 6u54 java5< 6u45 java8 < 8u122 或者java.rmi.server.useCodebaseOnly=false
|