RMI全称是Remote Method Invocation,远程⽅方法调⽤
是让某个Java虚拟机上的对象调⽤另一个Java虚拟机中对象上的⽅方法,只不不过RMI是Java独 有的⼀一种机制
本质是跨越调用JVM对象方法,跨网络的对象方法调用
RMI 的核心概念
- 远程对象(Remote Object) 运行在远程 JVM 上的对象,必须继承
UnicastRemoteObject
并实现Remote
接口 - 远程接口(Remote Interface) 声明远程调用的方法,必须继承
java.rmi.Remote
,且每个方法都要抛出RemoteException
- 存根(Stub) 客户端代理对象,屏蔽了底层网络通信的细节,把方法调用转发到远程对象
- 骨架(Skeleton)(Java 5 以前用,现在已合并进 Stub) 服务器端代理对象,负责接收请求并调用真正的远程对象
- RMI 注册表(RMI Registry) 一个命名服务,服务器可以把远程对象绑定到注册表里,客户端再通过名字查找
工作流程
定义远程接口(继承 Remote
)
实现远程接口(继承 UnicastRemoteObject
)
启动 RMI 注册表(rmiregistry)
服务器端绑定对象到注册表
客户端查找对象并调用方法
远程接口
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
String sayHello(String name) throws RemoteException;
}
服务端实现
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
public class Helloimpl extends UnicastRemoteObject implements Hello {
protected Helloimpl() throws RemoteException {
super();
}
public String sayHello(String name) throws RemoteException {
return "Hello, " + name + "!";
}
}
server
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Server {
public static void main(String[] args) {
try {
Helloimpl obj = new Helloimpl();
Registry registry = LocateRegistry.createRegistry(1099); // 启动RMI注册表
registry.rebind("HelloService", obj);
System.out.println("Server ready");
} catch (Exception e) {
e.printStackTrace();
}
}
}
client
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
public static void main(String[] args) {
try {
Registry registry = LocateRegistry.getRegistry("localhost", 1099);
Hello stub = (Hello) registry.lookup("HelloService");
String response = stub.sayHello("sun");
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行起来可以看到
我们运行Server类启动服务,运行Client类连接服务并调用远程方法,服务器处理请求并返回结果,最后客户端接收并回显
根据P牛的文章学习攻击
原理和流程我们已经大概了解了,那么我们就应该考虑安全问题了
能够访问到RMI Registry服务的情况下,如何进行攻击?
RMI Registry是一个远程对象管理的地方,可以理解为一个远程对象的“后台”。我们可以尝试直 接访问“后台”功能
但是java对远程访问RMI Registry进行了限制,只有来源地址是localhost的时候才能调用rebind,bind,unbind等方法
这些方法都是用于把远程对象obj绑定到注册表中的名字name上
其中list()方法,属于 java.rmi.registry.Registry
接口
作用是:返回一个 String[]
,里面是注册表中当前绑定的所有服务名
方法 | 功能 | 特点 |
---|---|---|
bind | 注册新服务 | 如果已存在同名,抛 AlreadyBoundException |
rebind | 注册/覆盖服务 | 如果已存在同名,会替换旧的 |
unbind | 删除注册表里的服务 | 如果名字不存在,抛 NotBoundException |
list | 查看注册表里所有绑定的服务 | 返回 String[] ,列出服务名称 |
如果目标服务器上存在危险方法,我们就可以通过RMI进行调用
…..