Java安全之ysoserial-JRMP模块分析(一)
Java安全之ysoserial-JRMP模块分析(一)
首发安全客:Java安全之ysoserial-JRMP模块分析(一)
0x00 前言
在分析到Weblogic后面的一些绕过方式的时候,分析到一半需要用到ysoserial-JRMP该模块。不止是Weblogic的反序列化漏洞会利用到,其他的反序列化漏洞也会利用到,所以在此对该模块做一个分析。了解底层原理,一劳永逸。但看到网上分析文章偏少,如有分析错误望师傅们指出。
概述
在这里简单来讲讲JRMP协议相关内容,JRMP是一个Java远程方法协议,该协议基于TCP/IP之上,RMI协议之下。也就是说RMI该协议传递时底层使用的是JRMP协议,而JRMP底层则是基于TCP传递。
RMI默认使用的JRMP进行传递数据,并且JRMP协议只能作用于RMI协议。当然RMI支持的协议除了JRMP还有IIOP协议,而在Weblogic里面的T3协议其实也是基于RMI去进行实现的。
RMI内容,具体参考:Java安全之RMI协议分析
0x01 JRMP模块利用
一、 ysoserial中的exploit/JRMPClient
是作为攻击方的代码,一般会结合payloads/JRMPLIstener
使用。
攻击流程如下:
- 需要发送
payloads/JRMPLIstener
内容到漏洞服务器中,在该服务器反序列化完成我们的payload后会开启一个RMI的服务监听在设置的端口上。 - 我们还需要在我们自己的服务器使用
exploit/JRMPClient
与存在漏洞的服务器进行通信,并且发送一个gadgets对象,达到一个命令执行的效果。(前面说过RMI协议在传输都是传递序列化,接收数据后进行反序列化操作。)
简单来说就是将一个payload发送到服务器,服务器反序列化操作该payload过后会在指定的端口开启RMI监听,然后通过exploit/JRMPClient
去发送攻击 gadgets对象。
二、第二种利用方式和上面的类似exploit/JRMPListener
作为攻击方进行监听,在反序列化漏洞位置发送payloads/JRMPClient
向我们的exploit/JRMPListener
进行连接,连接后会返回在exploit/JRMPListener
的gadgets对象并且进行反序列化
攻击流程如下:
攻击方在自己的服务器使用
exploit/JRMPListener
开启一个rmi监听往存在漏洞的服务器发送
payloads/JRMPClient
,payload中已经设置了攻击者服务器ip及JRMPListener监听的端口,漏洞服务器反序列化该payload后,会去连接攻击者开启的rmi监听,在通信过程中,攻击者服务器会发送一个可执行命令的payload(假如存在漏洞的服务器中有使用org.apacje.commons.collections
包,则可以发送CommonsCollections
系列的payload),从而达到命令执行的结果。
在前文中的 Java 安全之Weblogic 2017-3248分析文章中,用到的时候第二种方式进行绕过补丁。前文中并没有对该模块去做分析,只是知道了利用方式和绕过方式,下面对JRMP模块去做一个深入的分析。查看内部是如何实现该功能的。
0x01 payloads/JRMPListener
该链的作用是在反序列化过后,在指定端口开启一个JRMP Server。后面会配合到exploit/JRMPClient
连接并且发送payload。
利用链
下面来看一下他的利用链
/**
* 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
*
* Requires:
* - JavaSE
*
* Argument:
* - Port number to open listener to
*/
构造分析
首先需要查看一下yso里面是如何生成gadget对象的。
可以直接定位到getObject方法中。
getObject方法中前面第一行代码获取了外部传入进来的端口,转换成int类型。
这个比较简单,主要内容在下面这段代码中。
使用Reflections.createWithConstructor
方法传入三个参数获取到一个UnicastRemoteObject
的实例对象。传入的参数第一个是ActivationGroupImpl.class
,第二个是RemoteObject.class
,而第三个则是一个Object的数组,数组中里面是RemoteRef.class
,第四个是UnicastServerRef
传入了刚刚获取的端口的一个实例对象。
第一个参数使用的是 ActivationGroupImpl 是因为在利用的时候,本身就是利用的 UnicastRemoteObject 的 readObject 函数,第二个参数需要满足两个条件:
要为 UnicastRemoteObject 的父类
不能在创建的过程中有其他什么多余的操作,满足这两个条件的两个类是:RemoteObject、RemoteServer
最后具体是怎么获取到的UnicastRemoteObject
实例对象,这里需要调试跟踪一下。
UnicastServerRef分析
在此之前,先来看看new UnicastServerRef(jrmpPort)
的内部实现。先跟踪最里层的方法。
UnicastServerRef
的构造方法,内部会去再new一个LiveRef对象并且传入输入进来的端口的参数。
选择跟踪。
内部是new了一个ObjID,继续跟踪。
里面还会去new一个UID赋值给space成员变量,UID这里自然都知道是啥意思,这里就不跟了,而下面随机获取一个值赋值给objNum。
ObjID
ObjID
用于标识导出到RMI运行时的远程对象。 导出远程对象时,将根据用于导出的API来隐式或明确地分配一个对象标识符。构造方法:
ObjID()
生成唯一的对象标识符。
ObjID(int objNum)
创建一个“众所周知”的对象标识符。
执行完成后返回到这一步。
这里调用了构造方法的重载方法。选择跟踪一下。
到了这一步,var1的参数自然不用解释,而后面的则是传入的端口。
里面再一次调用重载方法,并且在传递的第二个参数调用了TCPEndpoint.getLocalEndpoint
并且传入端口进行获取实例化对象。继续跟踪。
内部调用getLocalEndpoint
重载方法,跟踪。
getLocalEndpoint
方法说明:
获取指定端口上本地地址空间的终结点。如果端口号为0,则返回共享的默认端点对象,其主机名和端口可能已确定,也可能尚未确定。
内部调用localEndpoints.get
方法并且传入var5,也就是TCPEndpoint的实例对象。
localEndpoints是一个map类型的类对象,这里get方法获取了var5,对应的value值,类型为LinkedList。这里获取到的是一个null。
执行到下一步
调用resampleLocalHost方法获取String的值,跟踪查看实现。
localHost的值是通过getHostnameProperty方法进行获取的。
执行完成后,返回到sun.rmi.transport.tcp#TCPEndpoint
,执行到一下代码中。
这里的代码比较容易理解,var为空,new一个TCPEndpoint对象,并且传入var7,var0,var1,var2。参数值是ip,端口,null,null。将该对象添加到var6里面。
后面则是对var3的对象进行赋值,ip和端口都赋值到var3的成员变量里面去。
最后就是调用localEndpoints.put(var5, var6);
讲var5, var6存储到localEndpoints
中。
最后进行返回var3对象。
执行完成后,回到这里
继续跟踪,构造方法的重载方法。
这里就没啥好说的了,就是赋值。
最后返回到外面入口的地方
调用了父类的构造方法
到了这里其实就已经跟踪完了。
yos利用链分析
返回到这一步跟踪Reflections.createWithConstructor
查看内部实现。
简化一下代码:
Constructor<? super T> objCons = RemoteObject.class.getDeclaredConstructor(new UnicastServerRef(jrmpPort));
其实也就是反射调用获取 RemoteObject参数为UnicastRef的构造方法。并且传递new UnicastServerRef(jrmpPort)
实例化对象作为构造方法参数。
而下面的setAccessible(objCons);
这个就不做分析了,分析过前面的利用链都大概清楚,这个其实就是修改暴力反射的一个方法类。
看到下面这段代码
这里进行跟踪。
其实借助ReflectionFactory.getReflectionFactory()
工厂方法在这里就是返回了ReflectionFactory的实例对象。
跟踪newConstructorForSerialization
方法
这里传递的var1 参数是ActivationGroupImpl.class
对象,而var2是刚刚反射获取的Constructor
对象。
下面是个三目运算,如果var2.getDeclaringClass() == var1
的话,返回var2,如果不低于的话,调用this.generateConstructor(var1, var2);
后的执行结果进行返回。
将代码简单化:
ActivationGroupImpl.class.getDeclaringClass()==ActivationGroupImpl.class ? var2
:this.generateConstructor(ActivationGroupImpl.class, var2)
这里调用了this.generateConstructor
方法并且传入了两个参数。后来才发现后面的这些内容是属于反射的底层实现,跟踪跑偏了。感兴趣的师傅们可以自行查看。
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
返回到这段代码,后来的查询资料发现newConstructorForSerialization
这个方法返回的是一个无参的constructor对象,但是绝对不会与原来的constructor冲突,被称为munged 构造函数
这里先来思考到一个问题,为什么不能使用反射直接调用呢?
其实并非所有的java类都有无参构造方法的,并且有的类的构造方法还是private的。所以这里采用这种方式进行获取。
再来看到上面的代码:
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
前面参数为ActivationGroupImpl.class
,指定获取ActivationGroupImpl.class
的 Constructor。后面的参数为反射获取RemoteObject的RemoteRef类型构造方法获取到的Constructor类。
最后将参数传递进行,返回创建一个ActivationGroupImpl
实例化对象。
执行完成回到这个方法内,发现该地方对ActivationGroupImpl
进行了向上转型为UnicastRemoteObject
类型
最后调用反射将UnicastRemoteObject
的实例对象的port字段修改成我们设置的端口的值。
0x02 调试分析
test类:
package ysoserial.test;
import ysoserial.payloads.JRMPClient;
import ysoserial.payloads.JRMPListener;
import java.io.*;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class test {
public static void main(String[] args) throws Exception {
JRMPListener jrmpListener = new JRMPListener();
UnicastRemoteObject object = jrmpListener.getObject("9999");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream bjos = new ObjectOutputStream(bos);
bjos.writeObject(object);
ByteArrayInputStream bait = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ojis = new ObjectInputStream(bait);
Object o = ojis.readObject();
}
}
这里是利用了UnicastRemoteObject
的readObject
作为反序列化的入口点。
在此处下断点开始调试分析。
readObject
方法处调用了reexport
方法,跟踪查看。
csf和ssf为空,执行到这里。
调用exportObject
方法并且传入this和port 这里的this,实际上是ActivationGroupImpl
,因为前面进行了向上转型。跟踪exportObject
。
这里再次调用重载方法,跟踪查看。
到了这一步,调用sref.exportObject
传入前面创建的实例对象。跟踪。
这里下面调用this.ref,而this.ref为LiveRef对象。这一段则是调用LiveRef.exportObject
。继续跟踪。
this.ep为Endpoint对象,这里调用的是Endpoint.exportObject
,这里的对象是怎么赋值的前面的构造分析的时候去讲过,这里不做多的赘述。
调用this.transport.exportObject;
继续跟踪。
到了这一步就调用了this.listen()
进行启动监听。
参考文章
ysoserial JRMP相关模块分析(一)- payloads/JRMPListener
0x03 结尾
JRMP的这个模块第一次分析还是挺费劲的,网上的相关资料也偏少。
Java安全之ysoserial-JRMP模块分析(一)的更多相关文章
- Java高级项目实战02:客户关系管理系统CRM系统模块分析与介绍
本文承接上一篇:Java高级项目实战之CRM系统01:CRM系统概念和分类.企业项目开发流程 先来CRM系统结构图: 每个模块作用介绍如下: 1.营销管理 营销机会管理:针对企业中客户的质询需求所建立 ...
- Java 安全之Weblogic 2018-2628&2018-2893分析
Java 安全之Weblogic 2018-2628&2018-2893分析 0x00 前言 续上一个weblogic T3协议的反序列化漏洞接着分析该补丁的绕过方式,根据weblogic的补 ...
- Java 9 揭秘(4. 模块依赖)
文 by / 林本托 Tips 做一个终身学习的人. 在此章节中,主要学习以下内容: 如何声明模块依赖 模块的隐式可读性意味着什么以及如何声明它 限定导出(exports)与非限定导出之间的差异 声明 ...
- Java安全之Cas反序列化漏洞分析
Java安全之Cas反序列化漏洞分析 0x00 前言 某次项目中遇到Cas,以前没接触过,借此机会学习一波. 0x01 Cas 简介 CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用 ...
- Ysoserial Commons Collections2分析
Ysoserial Commons Collections2分析 About Commons Collections2 CC2与CC1不同在于CC2用的是Commons Collections4.0; ...
- Ysoserial Commons Collections3分析
Ysoserial Commons Collections3分析 写在前面 CommonsCollections Gadget Chains CommonsCollection Version JDK ...
- Ysoserial Commons Collections7分析
Ysoserial Commons Collections7分析 写在前面 CommonsCollections Gadget Chains CommonsCollection Version JDK ...
- Java安全之C3P0利用与分析
Java安全之C3P0利用与分析 目录 Java安全之C3P0利用与分析 写在前面 C3P0 Gadget http base C3P0.getObject() 序列化 反序列化 Class.forN ...
- Linux下java进程CPU占用率高分析方法
Linux下java进程CPU占用率高分析方法 在工作当中,肯定会遇到由代码所导致的高CPU耗用以及内存溢出的情况.这种情况发生时,我们怎么去找出原因并解决. 一般解决方法是通过top命令找出消耗资源 ...
随机推荐
- CCNP之静态路由实验报告
静态路由实验报告 一.实验要求: 1.内网IP基于172.16.0.0/16自行子网划分 2.除了R2--R4路由器各有两个环回接口 3.R1下的PC自动获取IP地址 4 ...
- MySQL:判断逗号分隔的字符串中是否包含某个字符串 && 如何在一个以逗号分隔的列表中的一个字段中连接MySQL中的多对多关系中的数据
需求: sql语句中,判断以逗号分隔的字符串中是否包含某个特定字符串,类似于判断一个数组中是否包含某一个元素, 例如:判断 'a,b,c,d,e,f,g' 中是否包含 'a',sql语句如何 ...
- js Table表格选中一行变色或者多选 并获取值
使用JQ <script> let old, oldColor; $("#sp_body tr").click(function (i) { if (old) oldC ...
- 通过PHP代码将大量数据插入到Sqlite3
PHP代码 读入txt文件,并写入到sqlite数据库里 <?php date_default_timezone_set('PRC'); $pdo = new PDO('sqlite:db/qq ...
- 用anaconda的pip安装第三方python包
启动anaconda命令窗口: 开始> 所有程序> anaconda> anaconda prompt会得到两行提示: Deactivating environment " ...
- [LeetCode]234. Palindrome Linked List判断回文链表
重点是: 1.快慢指针找到链表的中点.快指针一次走两步,慢指针一次走一步,分清奇偶数情况. 2.反转链表.pre代表已经反转好的,每次将当前节点指向pre /* 快慢指针得到链表中间,然后用206题方 ...
- 01 . Go之从零实现Web框架(类似Gin)
设计一个框架 大部分时候,我们需要实现一个 Web 应用,第一反应是应该使用哪个框架.不同的框架设计理念和提供的功能有很大的差别.比如 Python 语言的 django和flask,前者大而全,后者 ...
- Kafka 消费组消费者分配策略
body { margin: 0 auto; font: 13px / 1 Helvetica, Arial, sans-serif; color: rgba(68, 68, 68, 1); padd ...
- volatile实现原理--为什么实现了可见性却不能保证原子性
本篇文章我们来解决一个问题 这也是面试面的比较多的问题,进阶阶段(高级)一般都会问到. volatile变量怎么保证可见性 为什么在并发情况下无法保证原子性? 比较懒了 摘了一段JVM原理的片段 ...
- @Transactional注解失效的解决方案
一.前言 开发中我们经常使用 @Transactional注解来启用Spring事务管理,但是如果使用方法不当,会遇到注解不生效该事务回滚的地方却没有回滚的问题. 总结下一般是以下几个原因: @Tra ...