首页 > 编程语言 >Java RMI分析与利用

Java RMI分析与利用

时间:2022-11-03 00:44:19浏览次数:72  
标签:调用 Java 利用 Registry RMI 远程 服务端 客户端

一. RMI介绍

RMI(Remote Method Invocation) 远程方法调用,顾名思义,是一种调用远程位置的对象来执行方法的思想。

在 Java 中,我们通常传递一个完整的对象,这个对象既包含数据,也包含数据和操作数据的方法,Java 中如果想完整的在网络中向远程位置传输一个对象,我们通常使用的方法是 Java 原生反序列化,并且可以结合动态类加载和安全管理器来安全的传输一个 Java 类

为了屏蔽网络通信的复杂性,RMI 引入了两个概念,分别是 Stubs(客户端存根) 以及 Skeletons(服务端骨架),当客户端(Client)试图调用一个在远端的 Object 时,实际调用的是客户端本地的一个代理类(Proxy),这个代理类就称为 Stub,而在调用远端(Server)的目标类之前,也会经过一个对应的远端代理类,就是 Skeleton,它从 Stub 中接收远程方法调用并传递给真实的目标类。Stubs 以及 Skeletons 的调用对于 RMI 服务的使用者来讲是隐藏的,我们无需主动的去调用相关的方法。但实际的客户端和服务端的网络通信时通过 Stub 和 Skeleton 来实现的

而具体的实现思想就是让我们获取远程主机上对象的引用,我们调用这个引用对象,但实际方法的执行在远程位置上

举例:客户端要调用服务端的A对象的A方法,客户端会生成A对象的代理对象,代理对象里通过用Socket与服务端建立联系,然后将A方法以及调用A方法是要传入的参数序列化好通过socket传输给服务端,服务端接受反序列化接受到的数据,然后通过反射调用A对象的A方法并将参数传入,最终将执行结果返回给客户端,给人一种客户端在本地调用了服务端的A对象的A方法的错觉。

RMI结构

RMI分为三部分

  • RMI Registry 注册中心,存放着远程对象的位置(ip、端口、标识符)
  • RMI Server 服务端,提供远程的对象
  • RMI Client 客户端,调用远程的对象

数据传输过程

这里使用Geekby大佬的一张图

整个过程中,Server到RMI Registry(步骤2)、Client与RMI Registry的双向通信(步骤3、4)和Client与Server的双向通信(步骤6、9),这三个过程数据传输都要经过序列化/反序列化的操作

RMI通信流程:服务端首先需要创建远程对象并在 Registry进行注册,客户端客户端连接Registry,并在其中寻找Name是hello的对象。然后Registry返回一个序列化的数据(Hello对象的初始引用)。这个hello对象是由一个动态代理生成的类,包含与Server通信的IP和端口。Client与该地址进行连接,在该连接中才真正调用远程⽅法。

RMI使用

使用 RMI ,首先要定义一个我们期望能够远程调用的接口,这个接口必须扩展 java.rmi.Remote 接口,用来远程调用的对象作为这个接口的实例,也将实现这个接口,为这个接口生成的代理(Stub)也是如此。这个接口中的所有方法都必须声明抛出 java.rmi.RemoteException 异常,例如:

package org.gk0d;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteInterface extends Remote {
    public String sayHello() throws RemoteException;
    public String sayHello(Object name) throws RemoteException;
    public String sayGoodbye() throws RemoteException;
}

然后创建这个远程接口的实现类,这个类中是真正的执行逻辑代码,并且通常会扩展java.rmi.server.UnicastRemoteObject 类,扩展此类后,RMI 会自动将这个类 export 给远程想要调用它的 Client 端,同时还提供了一些基础的 equals/hashcode/toString 方法。这里必须为这个实现类提供一个构造函数并且抛出 RemoteException

在 export 时,会随机绑定一个端口,监听客户端的请求,所以即使不注册,直接请求这个端口也可以通信,

如果不想让远程对象成为 UnicastRemoteObject 的子类,后面就需要主动的使用其静态方法 exportObject 来手动 export 对象

package org.gk0d;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RemoteObject extends UnicastRemoteObject implements RemoteInterface {

    protected RemoteObject() throws RemoteException {
    }

    @Override
    public String sayHello() throws RemoteException {
        return "Hello My Friend";
    }

    @Override
    public String sayHello(Object name) throws RemoteException {
        return name.getClass().getName();
    }

    @Override
    public String sayGoodbye() throws RemoteException {
        return "Bye";
    }
}

现在可以被远程调用的对象被创建好了,接下来如何调用呢?Java RMI 设计了一个 Registry 的思想,很好理解,我们可以使用注册表来查找一个远端对象的引用,更通俗的来讲,这个就是一个 RMI 电话本,我们想在某个人那里获取信息时(Remote Method Invocation),我们在电话本上(Registry)通过这个人的名称 (Name)来找到这个人的电话号码(Reference),并通过这个号码找到这个人(Remote Object)。

这种电话本的思想,由 java.rmi.registry.Registryjava.rmi.Naming 来实现。这里分别来说说这两个东西。
java.rmi.Naming,这是一个 final 类,提供了在远程对象注册表(Registry)中存储和获取远程对象引用的方法,这个类提供的每个方法都有一个 URL 格式的参数,格式如下: //host:port/name

  • host 表示注册表所在的主机
  • port 表示注册表接受调用的端口号,默认为 1099
  • name 表示一个注册 Remote Object 的引用的名称,不能是注册表中的一些关键字
    Naming 提供了查询(lookup)、绑定(bind)、重新绑定(rebind)、接触绑定(unbind)、list(列表)用来对注册表进行操作。也就是说,Naming 是一个用来对注册表进行操作的类。而这些方法的具体实现,其实是调用 LocateRegistry.getRegistry 方法获取了 Registry 接口的实现类,并调用其相关方法进行实现的。

java.rmi.registry.Registry 接口,这个接口在 RMI 下有两个实现类,分别是 RegistryImpl 以及 RegistryImpl_Stub
我们通常使用 LocateRegistry#createRegistry() 方法来创建注册中心

package org.gk0d;

import java.rmi.registry.LocateRegistry;

public class Registry {

    public static void main(String args[]) {
        try {
            LocateRegistry.createRegistry(1099);
            System.out.println("Server Start");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

然后将待调用的类进行绑定:

攻击面

客户端攻击服务端

服务端攻击客户端

客户端攻击注册中心

服务端攻击注册中心

注册中心攻击客户端(或服务端)

JEP290绕过

攻击方式后边再写

https://myzxcg.com/2021/10/Java-RMI分析与利用/
https://paper.seebug.org/1194/#_8
https://halfblue.github.io/2021/11/02/RMI反序列化漏洞之三顾茅庐-攻击实现/#more

标签:调用,Java,利用,Registry,RMI,远程,服务端,客户端
From: https://www.cnblogs.com/gk0d/p/16839758.html

相关文章

  • 关于Java的volatile关键字、内存屏障的思考
    内存屏障就是一种屏障指令,在X86架构中,指的是加了“lock前缀”的汇编指令在执行时会让CPU或编译器在对内存进行操作的时候,严格按照一定的顺序来执行。也就是说......
  • Java反射
    一Java安全可以从反序列化漏洞开始说起,反序列化漏洞⼜可以从反射开始说起正是反射使得Java拥有了动态特性,对象可以通过反射获取他的类,类可以通过反射拿到所有⽅法(包括私......
  • 学习Java的第三天 运算符
    运算符算术运算符注意事项需要注意的是i++和++i的区别i++运行完这行代码后在进行自增或自减++i在运行这行前就进行自增或者自减运算结果的数据类型为参与运算的......
  • Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化
    承接上文:Java函数式编程:一、函数式接口,lambda表达式和方法引用这次来聊聊函数式编程中其他的几个比较重要的概念和技术,从而使得我们能更深刻的掌握Java中的函数式编程。......
  • java生成pdf
    参考链接https://blog.csdn.net/weixin_39806100/article/details/86616041https://www.cnblogs.com/next-yrz/p/10134670.htmlhttps://www.likecs.com/show-306660785.......
  • java----GUI编程
    1packagecom.cilinmengye.HouseWork5;23importjavax.swing.*;4importjava.awt.*;5importjava.awt.event.ActionEvent;6importjava.awt.event.Actio......
  • java中string类的使用
    packagecom.te.jdkapi;/*字符串不可变。一经初始化就不会改变*/publicclassStudy_String{publicstaticvoidmain(String[]args){Stringa="1......
  • Java学习——10.02
     不得不说,Java真的很好上手,只不过搭建环境真的难。今天的话搞了一个喜欢的Java开发背景图,看着就心情愉悦。再加上今天idea学生认证下来,就很棒!    废话不多说进入正......
  • javascript - 练习题(若干)
    慢慢收集一些习题、考题练习1问:X,Y,Z分别是多少?varx=1,y=z=0;functionadd(n){returnn=n+1;}y=add(x);functionadd(n){returnn=n+3;}z=add(x);conso......
  • Java GUI编程(未完待续...)
    JavaGUIAWT1.组件和容器Frame弹出一个窗口packageuichuan;importjava.awt.*;importjava.awt.event.WindowAdapter;importjava.awt.event.WindowEvent;pu......