RMI反序列化分析

这篇具有很好参考价值的文章主要介绍了RMI反序列化分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

RMI介绍

RMI全程Remote Method Invocation (远程方法引用),RMI有客户端和服务端,还有一个注册中心,在java中客户端可以通过RMI调用服务端的方法,流程图如下:
RMI反序列化分析
服务端创建RMI后会在RMI Registry(注册中心)注册,之后客户端都是从注册中心调用方法,RMI分为三个主体部分:

  • Client-客户端:客户端调用服务端的方法
  • Server-服务端:远程调用方法对象的提供者,也是代码真正执行的地方,执行结束会返回给客户端一个方法执行的结果
  • Registry-注册中心:其实本质就是一个map,相当于是字典一样,用于客户端查询要调用的方法的引用,在低版本的JDK中,Server与Registry是可以不在一台服务器上的,而在高版本的JDK中,Server与Registry只能在一台服务器上,否则无法注册成功

RMI的简单使用

服务端

准备三个文件,接口,实现接口的类,服务端

package org.example.server;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RMIMethodServer extends Remote {
    public String sayHello(String key) throws RemoteException;
}
package org.example.server.impl;

import org.example.server.RMIMethodServer;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RMIMethodImpl implements RMIMethodServer {
    public RMIMethodImpl() throws RemoteException {
        UnicastRemoteObject.exportObject(this, 0);
    }

    @Override
    public String sayHello(String key){
        System.out.println(key.toUpperCase());
        return key.toUpperCase();
    }
}
package org.example;

import org.example.server.RMIMethodServer;
import org.example.server.impl.RMIMethodImpl;

import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer{
    public static void main(String[] args) throws RemoteException, AlreadyBoundException {
        RMIMethodServer remoteServer = new RMIMethodImpl();
        // 注册中心
        Registry r = LocateRegistry.createRegistry(7788);
        // 绑定对象到注册中心
        r.bind("remoteServer", remoteServer);
    }
}

客户端

package org.example;

import org.example.server.RMIMethodServer;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIClient {
    public static void main(String[] args) throws RemoteException, NotBoundException {
        Registry r = LocateRegistry.getRegistry("127.0.0.1", 7788);
        RMIMethodServer remoteServer = (RMIMethodServer) r.lookup("remoteServer");
        remoteServer.sayHello("I am handsome");
    }
}

先运行服务端,在运行客户端,可以看到服务端有输出
RMI反序列化分析

RMI创建流程分析

创建远程对象

第一步创建实现类对象
RMI反序列化分析
我们的实现类继承了UnicastRemoteObject
RMI反序列化分析
下一步进入UnicastRemoteObject的构造方法
RMI反序列化分析
导出我们的远程对象
RMI反序列化分析
继续跟进,又调用了另一个同名方法,UnicastServerRef是封装RMI的入口
RMI反序列化分析
跟进UnicastServerRef,构造方法调用了父类的构造方法,记住LiveRef这个对象,这是核心类,后面所用到的其它大部分类都是对这个类的封装
RMI反序列化分析
继续跟进LiveRef,又调用了另一个类的构造方法
RMI反序列化分析
这个ObjID没啥用,就是一个标识,继续跟进,到这才是核心,TCPEndpoint.getLocalEndpoint(port)这里才真正调用了远程服务对象的信息
RMI反序列化分析
继续跟进,到这,能看出一些关于网络请求的信息
RMI反序列化分析
继续跟进,回到LiveRef,这里有一些赋值RMI反序列化分析
执行完后就回到了UnicastServerRef,下一步对ref进行了赋值,这里是第一次封装RMI反序列化分析
最后回到了UnicastRemoteObject,可以看到sref就是LiveRef的封装
RMI反序列化分析
继续跟进,进入Util.createProxy这个方法
RMI反序列化分析
getClientRef获取LiveRef的封装
RMI反序列化分析
进入createProxy,加载远程类,判断这个类是否有_Stub后缀,这里显然没有
RMI反序列化分析
下面是一个创建动态代理的过程
RMI反序列化分析
创建完后回到stub = Util.createProxy(implClass, getClientRef(), forceStubUse);
然后创建一个Target类,这里其实就是把这一大堆东西又进行一次封装
RMI反序列化分析
接下来是exportobject的套娃,LiveRef -> TCPEndpoint -> TCPTransport,进入listen方法
RMI反序列化分析
是一个建立socket的过程,里面对port进行了一个初始化,随机指定了一个端口
RMI反序列化分析
listen过后又来到父类Transport的exportobjectRMI反序列化分析
跟进putTarget方法,将我们层层封装的Target放入一个Map(objtable)中结束
RMI反序列化分析

创建注册中心和绑定

进入createRegistry
RMI反序列化分析
进入RegistryImpl,我们用的不是默认端口1099,所以直接看else
RMI反序列化分析
new一个LiveRef,然后用UnicastServerRef封装,进入setup方法,调用exportobject,到这是不是很眼熟,我们创建远程对象的时候走过
RMI反序列化分析
进入createProxy方法,这次就跟上次不一样了,因为RegisryImpl_stub这个类存在,所以进入if
RMI反序列化分析
进入createStub方法,这里有个反射实例化的过程,实例化了这个RegisryImpl_stub
RMI反序列化分析
执行完后跳出去,这里设置了Skeleton(骨架)
RMI反序列化分析
从一开始的流程图我们知道,RMI是一个对称分布的结构,客户端有stub,服务端就对应有Skeleton,客户端通过stub请求远程方法,服务端就通过Skeleton去调用方法,然后通过Skeleton获取结果,最后传给客户端Stub,客户端就从Stub获取结果,因此继续跟进setSkeleton方法:
RMI反序列化分析
进入createSkeleton方法,反射获取Skeleton
RMI反序列化分析
之后又进入Target的构造方法,跟之前不同的地方是disp中的skel有值了
RMI反序列化分析RMI反序列化分析
接着又是exportobject的套娃,来到TCPTransport进入listen方法,创建socket,之后一系列的操作跟第一步相同,返回一个stub

客户端请求注册中心(客户端被攻击)

这里重点在注册后使用了lookup方法,我们调试进去,调试个鸡毛,这是个class文件
RMI反序列化分析
直接硬看
RMI反序列化分析

漏洞点一

先获取了一个输出流,获得服务端序列化结果,之后在后面调用readObject反序列化,这里一看就有漏洞,如果服务端的返回是恶意的,直接就被rce了

漏洞点二

super.ref.invoke这东西看着就像漏洞,这里的ref是UnicastRef,我们跟进去
RMI反序列化分析
call.executeCall是重点,这里call其实StreamRemoteCall,进入StreamRemoteCall查看executeCall方法,在末尾的异常处理调用了in.readObject,这里也能被攻击
RMI反序列化分析

漏洞点三

我们调用的是lookup方法,那其它方法是不是也能利用,一看,全都调用了invoke方法,攻击点都是漏洞点二

客户端请求服务端(客户端被攻击)

漏洞点分析

由于获取的remoteServer是动态代理类,所以会调用invoke方法RMI反序列化分析
跟进调试,RemoteObjectInvocationHandler
RMI反序列化分析
其中的invokeRemoteMethod,看名字知道是方法调用,跟进一看,有ref.invoke
RMI反序列化分析
跟进invoke,有个marshalValue
RMI反序列化分析
继续跟进,这是判断参数类型的,最后序列化,这里没判断String类型,所以用String类型可以到

RMI反序列化分析
出来了,又调用了call.executeCall(),上面的漏洞点二,攻击手法一样,然后会判断返回值是否为空
RMI反序列化分析
不为空调用unmarshalValue,进去一看,同样是判断类型,然后反序列化,这就能反序列化攻击了RMI反序列化分析

客户端请求服务端(注册中心被攻击)

创建注册中心的流程走一边走到listen里,看这个线程RMI反序列化分析
跟进AcceptLoop,注意这个executeAcceptLoop
RMI反序列化分析
继续跟进,这里的execute又开了一个线程池
RMI反序列化分析
继续跟进,进入ConnectionHandler的run方法,也就是run0方法
RMI反序列化分析
继续跟进,StreamRemoteCall这可太熟了,但是它不是重点
RMI反序列化分析
继续跟进serviceCall
RMI反序列化分析
放弃。。。这里怎么断点断不住,直接手动进去查看
RMI反序列化分析
进入dispatch方法,这里是UnicastServerRef的方法
RMI反序列化分析
跟进oldDispatch,skel是Registry_Impl_Skel
RMI反序列化分析
跟进dispatch方法,case 2中反序列化
RMI反序列化分析
然后这里case对应情况如下

  • bind : 0
  • list : 1
  • lookup : 2
  • rebind : 3
  • unbind : 4

如果客户端传入恶意对象,那注册中心就会被反序列化攻击

客户端请求服务端(服务端被攻击)

回到上面的oldDispatch处,过了这个继续往下,调用unmarshalValue,很熟RMI反序列化分析
判断是否为基本类型,传入Object,同样反序列化,寄

客户端请求服务端(DGC)

DGC就是RMI里垃圾回收机制,具体介绍如下:

分布式垃圾回收,又称 DGC,RMI 使用 DGC 来做垃圾回收,因为跨虚拟机的情况下要做垃圾回收没办法使用原有的机制。我们使用的远程对象只有在客户端和服务端都不受引用时才会结束生命周期。

而既然 RMI 依赖于 DGC 做垃圾回收,那么在 RMI 服务中必然会有 DGC 层,在 yso 中攻击 DGC 层对应的是 JRMPClient,在攻击 RMI Registry 小节中提到了 skel 和 stub 对应的 Registry 的服务端和客户端,同样的,DGC 层中也会有 skel 和 stub 对应的代码,也就是 DGCImpl_Skel 和 DGCImpl_Stub,我们可以直接从此处分析,避免冗长的 debug。

这个有点复杂,没太看懂,学会了再补充..............文章来源地址https://www.toymoban.com/news/detail-843630.html

到了这里,关于RMI反序列化分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Shiro反序列化分析

    Shiro,一个流行的web框架,养活了一大批web狗,现在来对它分析分析。Shiro的gadget是CB链,其实是CC4改过来的,因为Shiro框架是自带 Commoncollections 的,除此之外还带了一个包叫做 CommonBeanUtils ,主要利用类就在这个包里 https://codeload.github.com/apache/shiro/zip/shiro-root-1.2.4 编辑shiro/s

    2024年03月24日
    浏览(20)
  • Hessian反序列化分析

    RPC全称为 Remote Procedure Call Protocol (远程调用协议),RPC和之前学的RMI十分类似,都是远程调用服务,它们不同之处就是RPC是通过标准的二进制格式来定义请求的信息,这样跨平台和系统就更加方便 RPC协议的一次远程通信过程如下: 客户端发起请求,并按照RPC协议格式填充信

    2024年04月11日
    浏览(39)
  • JDBC反序列化分析

    找两个序列化后的bin文件,进行对比,可以发现前两个字节是固定的 AC , ED ,变十进制就是 -84 , -19 记住这两个数,后面分析的时候会用到 触发点在 com.mysql.cj.jdbc.result.ResultSetImpl.getObject() 可以看到在触发readObject之前还对data的前两个字节进行了比较来判断是不是序列化对象,

    2024年04月08日
    浏览(38)
  • SnakeYaml反序列化分析

    SnakeYaml是Java中解析yaml的库,而yaml是一种人类可读的数据序列化语言,通常用于编写配置文件等。yaml真是到哪都有啊。 SPI机制就是,服务端提供接口类和寻找服务的功能,客户端用户这边根据服务端提供的接口类来定义具体的实现类,然后服务端会在加载该实现类的时候去

    2024年04月22日
    浏览(16)
  • FastJson反序列化分析

    前言:网上关于FastJson的分析文章一大片,本文只是笔者在实践操作中理解的一些东西,不算特别详细,留作日后复习,欢迎一起交流 什么是FastJson? Fastjson是一个由阿里巴巴维护的一个json库。它采用一种“假定有序快速匹配”的算法,是号称Java中最快的json库。 先来看看一

    2024年02月06日
    浏览(22)
  • Resin反序列化链分析

    Resin是一个轻量级的、高性能的开源Java应用服务器。它是由Caucho Technology开发的,旨在提供可靠的Web应用程序和服务的运行环境。和Tomcat一样是个服务器,它和hessian在一个group里,所以有一定的联系 因为是JDNI,所以还是得注意下jdk版本,这里用jdk8u65 之前研究过Hessian反序列化

    2024年04月24日
    浏览(18)
  • Kryo反序列化链分析

    Kryo是一个快速序列化/反序列化工具,依赖于字节码生成机制(底层使用了ASM库),因此在序列化速度上有一定的优势,但正因如此,其使用也只能限制在基于JVM的语言上。 Kryo序列化出的结果,是其自定义的,独有的一种格式。由于其序列化出的结果是二进制的,也即byte[],因

    2024年04月14日
    浏览(17)
  • Groovy反序列化链分析

    Groovy 是一种基于 JVM 的开发语言,具有类似于 Python,Ruby,Perl 和 Smalltalk 的功能。Groovy 既可以用作 Java 平台的编程语言,也可以用作脚本语言。groovy 编译之后生成 .class 文件,与 Java 编译生成的无异,因此可以在 JVM 上运行。 在项目中可以引用 Groovy 的相关包依赖,分为核心

    2024年04月13日
    浏览(15)
  • Rome反序列化链分析

    先看看调用栈: 先给出poc,然后一步步调试分析 在readObject处打个断点开始调试 进入HashMap的readObject 跟进hash方法 跟进hashCode方法 来到ObjectBean的hashCode方法, _equalsBean 是EqualsBean的实例对象,跟进它的beanHashCode方法 _obj 是ToStringBean的实例对象,跟进它的toString方法 进入另一个

    2024年04月09日
    浏览(39)
  • Spring反序列化JNDI分析

    Spring框架的 JtaTransactionManager 类中重写了 readObject 方法,这个方法最终会调用到JNDI中的 lookup() 方法,关键是里面的参数可控,这就导致了攻击者可以利用JNDI注入中的lookup()参数注入,传入恶意URI地址指向攻击者的RMI注册表服务,以使受害者客户端加载绑定在攻击者RMI注册表服

    2024年04月08日
    浏览(21)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包