Java安全之RMI协议分析
Java安全之RMI协议分析
0x00 前言
在前面其实有讲到过RMI,但是只是简单描述了一下RMI反序列化漏洞的利用。但是RMI底层的实现以及原理等方面并没有去涉及到,以及RMI的各种攻击方式。在其他师傅们的文章中发现RMI的攻击方式很多。 所以在此去对RMI的底层做一个分析,后面再去对各种攻击方式去做一个了解。
0x01 底层协议概述
RPC
RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。
小总结:
在最原始数据通讯中其实还是归根到TCP/UDP协议,但是自使用了RPC后可以不需要了解底层网络协议,但是底层还是通过TCP/UDP去进行网络调用的。 其实RPC也只是远程方法调用的统称,重点在于方法调用中。而RMI实现就是Java版的一个RPC实现。
JMX
JMS:Java 消息服务(Java Messaging Service) 是一种允许应用程序创建、发送、接受和读取消息的Java API。JMS 在其中扮演的角色与JDBC 很相似, JDBC 提供了一套用于访问各种不同关系数据库的公共API,JMS 也提供了独立于特定厂商的企业消息系统访问方式。
JMS 的编程过程很简单,概括为:应用程序A 发送一条消息到消息服务器(也就是JMS Provider)的某个目的地(Destination),然后消息服务器把消息转发给应用程序B。因为应用程序A 和应用程序B 没有直接的代码关连。
RPC 和RMI的区别
RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC不依赖于具体的网络传输协议,tcp、udp等都可以。
RPC是跨语言的通信标准。
RMI可以被看作SUN对RPC的Java版本的实现,当然也还有其他的RPC
微软的DCOM就是建立在ORPC协议之上实现的RPC。
RMI集合了Java序列化和Java远程方法协议(Java Remote Method Protocol)。这里的Java远程方法协议则是JRMP。
RMI和JMS的区别
传输方式
- JMS 与 RMI 的区别在于:采用 JMS 服务,对象是在物理上被异步从网络的某个 JVM 上直接移动到另一个 JVM 上。
- RMI 对象是绑定在本地 JVM 中,只有函数参数和返回值是通过网络传送的。
方法调用
- RMI 一般都是同步的,也就是说,当client端调用Server端的一个方法的时候,需要等到对方的返回,才能继续执行client端,这个过程跟调用本地方法感觉上是一样的,这也是RMI的一个特点。
- JMS 一般只是一个点发出一个Message(消息)到Message Server端,发出之后一般不会关心谁用了这个message(消息)。
- 一般RMI的应用是紧耦合,JMS的应用相对来说是松散耦合的应用。
本段取自RMI与RPC的区别
由此得知其实RMI协议是发送方法以及方法参数,请求到server端后,server进行执行后返回结果给client端。
0x02 RMI底层架构
底层架构概念
根据上面内容其实可以总结为一句话,RPC是为了隐藏网络通信过程中的细节,方便使用。而在RMI中也是一样的。RMI中为了隐藏网络通讯的过程细节采用了动态代理的方式来进行实现。
下面先来看到调用流程图

在客户端和服务器各有一个代理,客户端的代理叫Stub(存根),服务端的代理叫Skeleton(骨架),合在一起形成了 RMI 构架协议,负责网络通信相关的功能。代理都是由服务端产生的,客户端的代理是在服务端产生后动态加载过去的。
stub担当远程对象的客户本地代表或代理人角色,负责把要调用的远程对象方法的方法名及其参数编组打包,并将该包转发给远程对象所在的服务器。

Stub编码后发送的数据包内容,包含如下内容:
1. 被使用的远程对象的标识符
2. 被调用的方法的描述
3. 编组后的参数
Skeleton接收到Stub发送数据会执行如下操作:
1. 从数据包中定位要调用的远程对象
2. 调用所需的方法,并传递客户端提供的参数
3. 捕获返回值或调用产生的异常。
4. 将打包返回值编组,返回给客户端Stub
本段内容部分取自:RMI反序列化漏洞分析
总结大体的内容如下:
客户端(Client):服务调用方。
客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。
服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。
服务端(Server):服务的真正提供者。
这里值得注意的一点是前面说到的传输进行打包和解包的步骤其实就是序列化和反序列化,传输的是序列化的数据。

架构调用流程
RMI大致的远程调用执行流程:
1. 客户端发起请求,请求转交至RMI客户端的stub类;
2. stub类将请求的接口、方法、参数等信息进行序列化;
3. 基于socket将序列化后的流传输至服务器端;
4. 服务器端接收到流后转发至相应的Skeleton类;
5. Skeleton类将请求的信息反序列化后调用实际的处理类;
6. 处理类处理完毕后将结果返回给Skeleton类;
7. Skelton类将结果序列化,通过socket将流传送给客户端的stub;
8. stub在接收到流后反序列化,将反序列化后的Java Object返回给调用者。
socket层中执行流程
server在远程机器上监听一个端口,这个端口是jvm或者os在运行时随机选择的一个端口。可以说server在远程机器上在这个端口上导出自己。
client并不知道server在哪,以及sever监听哪个端口,但是他有stub。stub知道所有这些东西,这样client可以调用stub上他想调用的任何方法。
client调用给你stub上的方法
stub链接server监听的端口并发送参数,详细过程如下:
4.1 client连接server监听的端口
4.2 server收到请求并创建一个socket来处理这个连接
4.3 server继续监听到来的请求
4.4 使用双方协定的歇息,传送参数和结果
4.5 协议可以是JRMP或者 iiop方法在远程server上执行,并发执行结果返回给stub
stub返回结果给client,就好像是stub执行了这个方法一样。
其实也是一样对于了下面的这张图

但是第二部分内容是值得思索的一点,为什么client不知道server的监听端口和server在哪,而stub却知道呢?client不知道server的host和port的话,stub是如何创建一个知道所有这一切的stub对象呢?
这时候就引出了RMIRegistry(注册中心)的作用了。
RMIRegistry作用
RMIRegistry 可以认为是一个服务,它提供了一个hashmap,里面是 public_name, Stub_object 名值对。比如有一个远程服务对象叫做 Scientific_Calculator,然后想把这个服务对外公布为 calc,这样会在server上创建一个stub对象,把他注册到RMIRegistry ,这样client就可以从RMIRegistry 中得到这个stub对象了,可以使用一个工具类java.rmi.Naming来方便的操作注册和操作。
实现可看Java安全之RMI反序列化该篇文章。
总结:
简单来说就是使用java.rmi.Naming将一个某一个类注册进RMIRegistry 里面,注册后会在server端创建stub对象,而client就可以从RMIRegistry 中得到这个stub对象。前面内容说到过代理都是由服务端产生的,客户端的代理是在服务端产生后动态加载过去的。RMIRegistry运行在server端当中。
RMIRegistry 执行流程详解
首先RMIRegistry 运行在server端,RMIRegistry 自身也是一个远程对象。有一点需要注意的是:所有的远程对象(继承了UnicastRemoteObject对象)都会在sever上任意的端口导出自己,因为RMIRegistry 也是一个远程对象,他也在server上导出自己,这个端口是1099。
服务端运行在server上,在UnicastRemoteObject构造函数里面,他把自己导出在server上一个任意端口上,这个端口client是不知道的
当你调用Naming.rebind()的时候,会传入一个CalcImpl 的引用(这里叫做 c)作为第2个参数,Naming 就会构造一个stub对象,详细如下:
a. Naming会使用getClass来获取类的名字,这里就是CalcImpl
b. 加上后缀_Stub 变成了 CalcImpl _Stub
c. 加载CalcImpl_Stub.class到虚拟机中
d. 从c中获取RemoteRef 对象e. 就是这个RemoteRef 对象中封装了服务端细节,包括服务端的hostname、port
f. 获取了RemoteRef 对象之后,就可以构造stub对象了。
g. 传递stud对象到RMIRegistry中进行绑定,即(publicname,stub)
f. RMIRegistry 中内部使用一个hashmap来存储(publicname,stub)当客户端使用 Naming.lookup()的时候,会传入public name 作为参数,RMIRegistry 就会返回stub给客户端调用
这里作者说的导出自己这个没太理解这个的意思,我的理解是映射,将内容映射到1099端口中。
总结:
总体来说就是所以的远程对象都会在server的任意的端口上映射,RMIRegistry 也会进行映射,但是RMIRegistry 映射的端口是1099(默认是,可以修改)。远程对象进行映射的端口,client是不知道的。但是stub对象会知道。
在调用Naming.rebind()并并且传入某一个引用的时候,Naming 就会构造一个stub对象。Naming内部采用getClass来获取类的名字,并且添加_Stub后缀后价值到虚拟机中,然后从引用中获取 RemoteRef 对象,该对象就封装了封装了服务端的细节。而获取到该RemoteRef 就可以构造stub对象了,构造完成后传递到RMIRegistry注册中心中进行绑定,内部采用hashmap键值对的方式,即(publicname,stub),这时候使用Naming.lookup()传入对应的方法名,则会返回对应的stub到client端。
RMIRegistry 存在的意义只是为了方便client获取到stub对象,stub构造函数中需要一个RemoteRef 对象,这个对象只能在server端获取。
本段内容部分摘取自:深入理解rmi原理
0x03 结尾
简单的分析了一下RMI底层的架构,但是这些其实都仅仅是基于概念和理论层面的,具体的代码实现其实还没去看。在其中也是看得晕头转向的,部分也摘取了其他师傅们的文章内容,感觉已经总结很到位了,疯狂安利。摘取的内容下也贴出来 摘取内容的出处,感谢各位师傅们的详细讲解。

Java安全之RMI协议分析的更多相关文章
- Java安全之ysoserial-JRMP模块分析(一)
Java安全之ysoserial-JRMP模块分析(一) 首发安全客:Java安全之ysoserial-JRMP模块分析(一) 0x00 前言 在分析到Weblogic后面的一些绕过方式的时候,分析到 ...
- Java学习---RMI 技术分析[Hessian]
一.什么是Hessian Hessian 是一个基于 binary-RPC 实现的远程通讯 library.使用二进制传输数据.Hessian通常通过Web应用来提供服务,通过接口暴露.Servlet ...
- ref:Java安全之反序列化漏洞分析(简单-朴实)
ref:https://mp.weixin.qq.com/s?__biz=MzIzMzgxOTQ5NA==&mid=2247484200&idx=1&sn=8f3201f44e ...
- Java安全之RMI反序列化
Java安全之RMI反序列化 0x00 前言 在分析Fastjson漏洞前,需要了解RMI机制和JNDI注入等知识点,所以本篇文来分析一下RMI机制. 在Java里面简单来说使用Java调用远程Jav ...
- Java安全之Weblogic 2016-0638分析
Java安全之Weblogic 2016-0638分析 文章首发先知:Java安全之Weblogic 2016-0638分析 0x00 前言 续上篇文的初探weblogic的T3协议漏洞,再谈CVE- ...
- Java 安全之Weblogic 2017-3248分析
Java 安全之Weblogic 2017-3248分析 0x00 前言 在开头先来谈谈前面的绕过方式,前面的绕过方式分别使用了streamMessageImpl 和MarshalledObject对 ...
- Java安全之Weblogic 2018-3248分析
Java安全之Weblogic 2018-3248分析 0x00 前言 基于前面的分析,后面的还是主要看补丁的绕过方式,这里就来简单的记录一下. 0x01 补丁分析 先来看看补丁细节 private ...
- Java安全之SnakeYaml反序列化分析
Java安全之SnakeYaml反序列化分析 目录 Java安全之SnakeYaml反序列化分析 写在前面 SnakeYaml简介 SnakeYaml序列化与反序列化 常用方法 序列化 反序列化 Sn ...
- 协议分析TMP
最近闲来有事, 分析了一个非常低端(非常低端的意思是说你不应该对她是否能取代你现有的QQ客户端作任何可能的奢望,她只是一个实验性的东西)的手机QQ的协议, 是手机QQ3.0, 所用到的TCP ...
随机推荐
- 求求你,别再用wait和notify了!
Condition 是 JDK 1.5 中提供的用来替代 wait 和 notify 的线程通讯方法,那么一定会有人问:为什么不能用 wait 和 notify 了? 哥们我用的好好的.老弟别着急,听 ...
- 2020-2021-1 20209307《Linux内核原理与分析》第七周作业
这个作业属于哪个课程 <2020-2021-1Linux内核原理与分析)> 这个作业要求在哪里 <2020-2021-1Linux内核原理与分析第七周作业> 这个作业的目标 & ...
- Windows下anaconda换源和pip换源
换源解决下载安装速度慢的问题. 1. anaconda换源 打开cmd命令行,输入 conda config --set showchannelurls yes 会在C:\Users\xx文件夹下生成 ...
- DRF对Django请求响应做了技术升级
Django视图是用来处理请求和响应的,Django默认是按Form和Template来设计的,如果要处理以JSON格式为主的RESTful API,那么就需要对Django请求和响应的处理代码进行优 ...
- react第十一单元(受控组件和非受控组件-实现类似于vue双向绑定的功能)
第十一单元(受控组件和非受控组件-实现类似于vue双向绑定的功能) #课程目标 理解因为react的单向数据流 理解表单组件会因为react数据流变的不好维护 理解受控组件与非受控组件的实质区别 理解 ...
- day112:MoFang:种植园使用websocket代替http&服务端基于flask-socketio提供服务&服务端响应信息&种植园页面显示初始化
目录 1.种植园使用websocket代替http 2.服务端基于socket提供服务 3.服务端响应信息 4.种植园页面展示 1.种植园使用websocket代替http 我们需要完成的种植园,是一 ...
- 技术基础 | 改进版的Apache Cassandra客户端请求路由
最近我们在客户端的驱动程序中引入了一些变更,这些变更会影响传入的请求在Apache Cassandra集群内的分发方式. 新的默认负载均衡算法即将随驱动程序推出,这些算法将有助于缩短长尾延迟,并提 ...
- 彻底理解Spring如何解决循环依赖
Spring bean生命周期 可以简化为以下5步. 1.构建BeanDefinition 2.实例化 Instantiation 3.属性赋值 Populate 4.初始化 Initializati ...
- tomcat8默认umask已改为027
1. umask的简单介绍 不过我们通常只用后三位,同样对应属主属组以及其他用户的权限,例如你的账号umask值为0022(可直接通过umask命令查看), 此时你创建的文件权限默认为644(文件初始 ...
- wildfly 21的domain配置
目录 简介 wildfly模式简介 domain controller的配置 Host controller的配置文件 忽略域范围的资源 Server groups Servers 总结 简介 wil ...