首页 > 编程语言 >java JRMP学习

java JRMP学习

时间:2024-09-04 17:27:50浏览次数:4  
标签:序列化 java RMI JRMP 学习 exploit JRMPClient JRMPListener payload

Java JRMP反序化

RMI依赖的通信协议为JRMP(Java Remote Message Protocol ,Java 远程消息交换协议),该协议为Java定制,基于TCP/IP之上,RMI协议之下,当需要进行RMI远程方法调用通信的时候要求服务端与客户端都为Java编写。、

这个协议就像HTTP协议一样,规定了客户端和服务端通信要满足的规范,RMI底层默认使用的JRMP进行传递数据,并且JRMP协议只能作用于RMI协议。

通过DGCImpl来实现攻击的也有两种,DGCImpl_Stub#dirty(服务端攻击客户端),还有个就是DGCImpl_Skel#dispatch(客户端攻击服务端)

之间看 DGCImpl_Skel#dispatch 方法:

存在个 case1 和 case2,分别对应了 cleardirty 方法,和 RgestryImpl_Skel#dispatch 中异曲同工。而且这里的两种都有反序列化,

ysoserial程序分析

payload/JRMPListener

调用链

UnicastRemoteObject.readObject()
UnicastRemoteObject.reexport()
    UnicastRemoteObject.exportObject()
        UnicastServerRef.exportObject()
            LiveRef.exportObject()
                TCPEndpoint.exportObject()
                    TCPTransport.exportObject()
                        TCPTransport.listen()

通过 createWithConstructor 方法来实例化了一个 UnicastRemoteObject 对象,

看到就是通过反射调用进行实例化对象,看到其第四个参数是构造函数所需的具体参数也就是 consArgs,跟进第四个参数 UnicastServerRef 构造方法,

调用了其父类的构造方法,继续跟进。

调用了其其他构造方法。

嗯,和前面的 rmi 服务类注册没什么区别,TCPEndpoint.getLocalEndpoint(port) 就是一些对网络请求的处理,继续向下

其实也就没什么了,结束返回对象 UnicastServerRef,其中包含的 ref 如下

接着就是进入createWithConstructor方法了,这个就是刚才说的通过反射来进行实例化对象的方法,

首先进行获取consArgsTypes类型参数的构造方法,传入的constructorClassRemoteObject,所以获得的其构造方法。

然后执行 ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons)

这样就可以绕过构造器直接进行实例化并且不会对进行 ref 进行初始化。最后获得实列 ActivationGroupImpl,这里还向上转型了 UnicastRemoteObject(这样可以避免直接实例化 UnicastRemoteObjec t对象直接触发监听)。

当正常对 UnicastRemoteObject 反序列化,会发现端口并不是指定的,而是一个随机端口,最后通过反射进行修改,传入的参数就是端口

Reflections.getField(UnicastRemoteObject.class, "port").set(uro, jrmpPort);

到这里构造JRMP Listener的payload就已经结束了。

payload/JRMPClient

反序列化UnicastRef类,UnicastRef实现了RemoteRef接口,RemoteRef接口又实现了Externalizable接口,Externalizable接口又实现了Serializable接口(这个简单跟进一下就知道了)

其中在 Externalizable 接口定义了 writeExternalreadExternal 方法,

这两个方法分别实现了序列化和反序列化,UnicastRef 类中对这两个方法进行了重写。

先看 UnicastRef.readExternal 方法:

调用了LiveRef.read方法,跟进

其中 TCPEndpoint.readHostPortFormat 就是获取host和port,返回一个封装了host和port的TCPEndpoint。

然后看到 new 了个 ref 对象。以前 RMI 中经常遇见这个对象,里面就是封装的一些 host,port,objid 等信息。看到如果输入流的类型就可以不是 ConnectionInputStream 类型(这个输出流我们是可以进行控制的),那么就会进入else语句

调用 DGCClient.registerRefs() 方法,跟进该方法

看到调用 lookup 方法,然后返回的 EndpointEntry 对象调用 registerRefs() 方法,在该方法结尾处调用

跟进makeDirtyCall方法,其中会调用DGC.dirty方法

实际会调用 DGCImpl_Stub.dirty 方法,这个方法下调用了newCall 方法建立一次连接,还会会对remoteCall进行一次反序列化

所以这里利用下面方法方法创建了个 UnicastRef 对象

ObjID id = new ObjID(new Random().nextInt()); // RMI registry  
TCPEndpoint te = new TCPEndpoint(host, port);  
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));

因为创建 UnicastRef 对象需要 LiveRef 对象,而 LiveRef 对象里的参数有需要 TCPEndpoint 对象。

然后创建 UnicastRef 的动态代理。

RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);  
Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {  
    Registry.class  
}, obj);

这个类的关键代码就是这些了。

exploit/JRMPListener

exploit/JRMPListener开启监听,客户端向exploit/JRMPListener进行连接时,会返回给客户端一个序列化对象,服务器接收一个对象后会进行反序列化操作

这个恶意对象就是这里payloadObject

有什么办法让客户端主动向exploit/JRMPListener进行连接?

那就要用到payload/JRMPClient,UnicastRef对象封装了host、port等信息,反序列化UnicastRef对象后,会触发DGC通信,与指定host的指定port进行连接

导致恶意服务端传输恶意数据流,在客户端造成反序列化

exploit/JRMPClient

而在ysoserial中,exploit/JRMPClient调用了makeDGCCall

主要为了调用dirty方法触发反序列化,传递一个用于反序列化的对象

最后在远程DGC层触发反序列化,以达到攻击远程DGC层的目的

关于JRMP的两种攻击流程如下

第一种攻击方式

个人理解:基于RMI的反序列化中的客户端打服务端的类型

我们需要先发送指定的payload(JRMPListener)到存在漏洞的服务器中,使得该服务器反序列化完成我们的payload后会开启一个RMI的服务监听在设置的端口上。

我们还需要在我们自己的服务器使用exploit(JRMPClient)与存在漏洞的服务器进行通信,并且发送一个利用链,达到一个命令执行的效果。

简单来说就是将一个payload(JRMPListener)发送到存在漏洞的服务器,存在漏洞的服务器反序列化操作该payload(JRMPListener)过后会在指定的端口开启RMI监听,然后再通过exploit(JRMPClient) 去发送利用链载荷,最终在存在漏洞的服务器上进行反序列化操作。

第二种攻击方式

个人理解:基于RMI的反序列化中的服务端打客户端的类型,这种攻击方式在实战中比较常用

将exploit(JRMPListener)作为攻击方进行监听。

我们发送指定的payloads(JRMPClient)使得存在漏洞的服务器向我们的exploit(JRMPListener)进行连接,连接后exploit(JRMPListener)则会返回给存在漏洞的服务器序列化的对象,而存在漏洞的服务器接收到了则进行反序列化操作,从而进行命令执行的操作。

PS:这里的payload和exploit就是指的不同包下的JRMPListener和JRMPClient!

第一种攻击方式(客户端攻击服务端)

payloads.JRMPListener+exploit.JRMPClient

看到上面 yso 中的四个类分析,可以先利用 payloads.JRMPListener 让客户端开启监听端口,在 yso 的运行下,这个对象将会被序列化处理,然后被进行传输,那么既然被序列化了,那么肯定是需要被触发的。

先通过yso来进行生成这个序列化对象:java -jar ysoserial-0.0.6-SNAPSHOT-all.jar JRMPListener 1099 > payload1.txt

然后创建个可以进行反序列化的服务器来继续测试

package org.example;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
  
public class jrmptest {  
    public static void main(String[] args) {  
        deserialize();  
    }  
  
    public static void serialize(Object obj) {  
        try {  
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("jrmplistener_payload.txt"));  
            os.writeObject(obj);  
            os.close();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    public static void deserialize() {  
        try {  
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("D:\\yingwenmingthree\\ysoserial-master\\target\\payload1.txt"));  
            is.readObject();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

运行

虽然这里没有什么显示,但是去查看端口发现 1099 端口已经开始监听了。

说明已经成功反序列化了我们的 payload1.txt,

具体RMI服务端和注册端如何绑定远程对象的详细过程参考:https://www.cnblogs.com/zpchcbd/p/13517074.html

当前面的payloads/JRMPListener作用了之后,那么对方就已经开启了RMI服务,接下来我们就可以通过exploit/JRMPClient发送gadgets来进行利用了(前提对方存在可以利用的gadgets)

该方法中的两个注解:

一、其功能和 RMIRegistryExpoit 类似,但是 RMIRegistryExpoit 主要目标是 rmi 的 Registry 模块,而 JRMPClient攻击的目标是的 rmi 中的 DGC 模块(Distributed Garbage Collection),只要有RMI服务监听了,都会有DGC的存在!

二、因为它Client全都是向server发送数据,没有接受过任何来自server端的数据。在 exploit/JRMPListener 和 payloads/JRMPClient 的利用过程中,这个 server 端和 client 端,攻击者和受害者的角色是可以互换的,在你去打别人的过程中,很有可能被反手一下,所以最好的情况就是,只是发送数据,不去接受另一端传过来的信息,所以说用这个 exploit/JRMPClient 是不会自己打自己的)

先来看下yso的exploit/JRMPClient的攻击复现,这里接着上面反序列化了payload/JRMPListener模块,开启了一个1099端口

java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPClient 127.0.0.1 1099 CommonsCollections5 calc

直接就攻击成功了。

其调用栈

其实该 payload 最主要的就是利用的 DGCImpI_Skel 的 dispatch 方法中的分支的反序列化进行攻击。剩下的我感觉其实就是 RMI 服务端接收来自客户端的数据的一些处理。

第二种攻击方式(服务端攻击客户端)

exploit.JRMPListener+payloads.JRMPClient

这个服务端打客户端的类型比起客户端打服务端的类型会更加的常用,一方面是外连,另一方面更多的因为是该 exploit/JRMPListener+payloads/JRMPClient 还存在绕过 jep290 的限制,所以往往会更加的通用。

payloads.JRMPClient:携带指定的攻击机 ip 和端口生成受害机第一次反序列化(需要代码中存在一个反序列化点)时的payload,受害机反序列化该payload时会向指定的攻击机ip+端口发起RMI通信,在通信阶段攻击机会将第二次反序列化的payload(如CommonCollections1)发送给受害机,此时发生第二次反序列化,执行真正的恶意命令。

找到一个反序列化点,然后将其payloads/JRMPClient发送,自己本地开启一个exploit/JRMPListener监听,如果不在JEP290的限制下的话,就能攻击成功

参考:https://www.cnblogs.com/zpchcbd/p/14934168.html

参考:https://xz.aliyun.com/t/12780

标签:序列化,java,RMI,JRMP,学习,exploit,JRMPClient,JRMPListener,payload
From: https://www.cnblogs.com/gaorenyusi/p/18396965

相关文章

  • MarkDown学习Day01
    Markdown学习标题标题:“#”+“标题”二级标题:“##”+“标题”三级标题:“###”+“标题”以此类推,最多只支持到六级标题字体Hello,World!Hello,World!Hello,World!Hello,World!Hello,World!引用嘿嘿,猫猫,嘿嘿“>”分割线三个“---”三个“***”图片英文“![图片名字......
  • 适合初学者学习的常用英语短语
    对于英语初学者来说,掌握一些基础且常用的短语对提升日常交流能力非常有帮助。以下是一些简单实用的英语短语,配以中文解释,适合初学者学习:Hello,howareyou?-你好,你好吗?I'mfine,thankyou.Andyou?-我很好,谢谢。你呢?Nicetomeetyou.-很高兴见到你。Nicetomeetyoutoo......
  • day04_编译原理学习
    第四章语法分析自顶向下分析的概述处理文法的编译器大致分为三种类型:通用型,自顶向下型和自底向上型。编译器中常用的方法可以分为自顶向下和自底向上。自顶向下分析从分析树的顶部(根节点)向底部(叶节点)方向构造分析树可以看成是从文法开始符号S推导出词串w的过程。每一步推......
  • java常用关键字
    类别关键字说明访问控制private私有的protected受保护的public公共的default默认类、方法和变量修饰符abstract声明抽象class类extends扩充、继承final最终值、不可改变的implements实现(接口)interface接口native本地、原......
  • 【Python篇】详细学习 pandas 和 xlrd:从零开始
    文章目录详细学习`pandas`和`xlrd`:从零开始前言一、环境准备和安装1.1安装`pandas`和`xlrd`1.2验证安装二、`pandas`和`xlrd`的基础概念2.1什么是`pandas`?2.2什么是`xlrd`?三、使用`pandas`读取Excel文件3.1读取Excel文件的基础方法代码示例:读取......
  • 三水的计算机网络学习之旅----实例探索如何来分层处理
    主机A要访问某个Web服务器1.首先在浏览器地址栏中输入Web服务器的域名,2.紧接着主机向Web服务器发送一个请求报文,3.服务器收到请求报文后执行相应操作,然后给主机发送响应报文4.主机收到响应报文后由浏览器负责解析与渲染。我们从五层原理体系来进行进一步解析:封装过程:(自......
  • java 二次反序列化
    java二次反序列化SignedObject该类是java.security下一个用于创建真实运行时对象的类,更具体地说,SignedObject包含另一个Serializable对象。先看其构造函数方法。看到参数接受一个可序列化的对象,然后又进行了一次序列化,继续看到该类的getObject方法(这是个getter方法......
  • Java之数据类型扩展及面试题讲解
    整数代码运行前运行后浮点数一些面试讲解关于银行业务怎么表示?钱如果我们用float和double来表示的话运行前运行后可以看出都是0.1,然而运行后却出现false(错误)的标志运行前运行后可以看出d2=d1=1,所以d2与d1不相等,然而却出现了true(正确)首先浮点数表现......
  • 【学习】【JavaScript 安全】JS代码混淆技术
    一、布局混淆1.1删除无效代码1.2标识符重命名二、数据混淆2.1数字混淆2.1.1进制转换2.1.2数学技巧2.1.3数字拆解2.2布尔混淆2.2.1类型转换2.2.2构造随机数2.3字符串混淆2.4undefined和null混淆......
  • 深度学习-用神经网络NN实现足球大小球数据分析软件
    文章目录前言一、数据收集1.1特征数据收集代码实例二、数据预处理清洗数据特征工程:三、特征提取四、模型构建五、模型训练与评估总结前言预测足球比赛走地大小球(即比赛过程中进球总数是否超过某个预设值)的深度学习模型是一个复杂但有趣的项目。这里,我将概述一个......