前言

在java的学习过程中,少不了对 ysoserial 的使用和学习,这里记录了整个 JRMP 相关的分析,分享出来,有什么地方不对的请大哥们直接喷就对了 ; )

简介

这个 JRMPListener 是 ysoserial 工具里的其中一个利用模块,作用是通过反序列化,开启当前主机的一个 JRMP Server ,具体的利用过程是,将 反序列化数据 发送到 Server 中,然后 Server 中进行反序列化操作,并开启指定端口,然后在通过 JRMPClient 去发送攻击 payload

流程

其实触发流程已经在 playloads/JRMPListener 里写出来了,如下:

被序列化的类是 ActivationGroupImpl ,反序列化的也是他,里面包括了RemoteObject(构造函数) 、 RemoteRef(构造函数的参数类型) 、 UnicastServerRef(构造函数的具体参数)

RemoteObject 的构造函数(paramType为RemoteRef)是最先被创建的,UnicastServerRef 是最后在返回 ActivationGroupImpl 实例的时候被使用的 sc.newInstance( UnicastServerRef )

平日里接触到的反序列化利用不都是,要么重写了 readObject ,要么像是 RMI 的利用那样,利用 Proxy 类中一个 InvocationHandler 成员的反序列化过程来实施攻击( invacationHandler 其实可以指定为那个 AnnotationInvocationHandler 的),那么这里的 JRMP 又是咋回事的.....感觉完全没有以上两者的影子

他用 ActivitionGroupImpl 来当做想要得到的对象,原因是为了得到一个 UnicastRemoteObject,跟踪了一下, UnicastRemoteObject 只有几个子类:

最合适的当然是 ActivationGroupImpl,其次,真正关键的流程在于 UnicastRemoteObject 的 readObject 函数和 reexport 函数,他会把接收到的序列化的 UnicastRemoteObject 类进行反序列化,并且重新 export 出去

在 UnicastServerRef 的 exportObject 中已经后续流程里,就是在研究 RMI 过程中的服务端开启监听端口的整个流程

简单来说,最主要的是 UnicastRemoteObject 自己实现了 readObject ,并且将会重新开启 JRMP 的监听服务,反序列化利用的就是这一点,然后 UnicastRemoteObject 的直接子类(同一路径下)是 ActivationGroupImpl , 所以要先得到 ActivationGroupImpl 的一个实例,并且将其序列化,设置的端口是单独设置的

然后感觉这个 port 的设置,主要在于这一句:

但是我把payload 的生成方式改了一下,不使用 UnicastRemoteObject 的子类 ActivationGroupImpl ,直接使用 UnicastRemoteObject 也是可以反序列化后监听端口成功的

分析总结

payloads/JRMPListener
利用的是 UnicastRemoteObject 里的 readObject 函数,在其反序列化的时候,此 readObject 函数会被调用

其中 UnicastServerRef 的 exportObject 函数就是之前研究过的 RMI 的 RMI Server 端的东西,后面的触发流程在 RMI 的相关笔记里有

然后就是依靠 newConstructorForSerialization 函数创建出的 UnicastRemoteObject ,其需要的参数有四,其一是需要得到的对象类,其二是需要得到的类的构造函数所在的类(第一个条件的父类),其三是所需要的构造子的参数类型(便于从父类中查找对应的构造函数),其四就是构造函数所需的具体参数

在 JRMPListener 中,第一个参数使用的是 ActivationGroupImpl ,对其跟踪后发现,本身就是利用的 UnicastRemoteObject 的 readObject 函数,所以将第一参数改为 UnicastRemoteObject 也未尝不可,第二个参数不能乱选,满足两个条件,一要为 UnicastRemoteObject 的父类,二要不能在创建的过程中有其他什么多余的操作,满足这两个条件的两个类是:RemoteObject、RemoteServer,通过实验发现,使用这两个类没啥区别....第三个参数和第四个参数都是根据第二个参数来的,所以不做解析

最后,序列化的是 UnicastRemoteObject 或者是其子类,并将其 port 属性设置为想要开通的端口值,那么发送到目标主机后,经过其反序列化就会发现,UnicastRemoteObject 的 readObject 被调用了,然后紧跟着调用了 reexport 函数.....

其实这么来看的话,还是不偏离反序列化那两个利用的条件(满足其一即可)

  1. 一个将要被反序列化的类其中的成员是一个 gadget ,在反序列化的时候调用了它的自定义 readObject .... 例子: RMI 的反序列化 payload
  2. 将要被反序列化的类自己或者是其父类中,有自定义过 readObject 的情况,并且 readObject 可以用来搞事情..... 例子: AnnotationInvocationHandler 的利用,还有这个 JRMPListener 的利用,都是要么自己实现了 readObject 函数,要么父类实现了这个函数,并且在函数中可以达到某些目的

一个小问题

这里有个疑问,既然 RMI 也是根据 JRMP 实现的,那么能不能使用 RMI Client 去打 payload 呢(指的是向目标机发送 exploit/RMIRegistryExploit ,然后自己服务器挂起 exploit/JRMPListener)

问题答案

应该是可以的,因为 exploit/JRMPListener 利用的是,最终 client 里接受 server 端反馈的异常,JRMPListener 是将 payload 写入了反馈异常中的一个成员变量里的.....

不过呢,在本地测试中,并没有弹出计算器,有点问题如下:

显然,目标机中报错了,但是明明这个 BadAttributeValueExpException 就是 exploit/JRMPListener 构造的呀,应该是已经反序列化成功了

经过测试,不使用 ysoserial 里的 RMIRegistryExploit ,我自己去写一个 rmi 的时候,也会弹出计算器

n1nty 师傅说是 java 沙盒的原因,查看 ExecCheckingSecurityManager 里的 checkExec,限制了命令执行

那么意思就是,如果直接使用自定义的 rmi 反序列化利用工具打过去,没有设置沙盒的话,可能会被人家反打一波........因为这个 JRMPListener 就是一个很好的例子,我开启一个监听端口后,等着你来打,然后反手就是一耳光......

源链接

Hacking more

...