Java安全之ysoserial-JRMP模块分析(一)

Java安全之ysoserial-JRMP模块分析(一)首发安全客:Java安全之ysoserial-JRMP模块分析(一)0x00前言在分析到Weblogic后面的一些绕过方式的时候,分析到

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

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使用。

攻击流程如下:

  1. 需要发送payloads/JRMPLIstener内容到漏洞服务器中,在该服务器反序列化完成我们的payload后会开启一个RMI的服务监听在设置的端口上。
  2. 我们还需要在我们自己的服务器使用exploit/JRMPClient与存在漏洞的服务器进行通信,并且发送一个gadgets对象,达到一个命令执行的效果。(前面说过RMI协议在传输都是传递序列化,接收数据后进行反序列化操作。)

简单来说就是将一个payload发送到服务器,服务器反序列化操作该payload过后会在指定的端口开启RMI监听,然后通过exploit/JRMPClient 去发送攻击 gadgets对象。

二、第二种利用方式和上面的类似exploit/JRMPListener作为攻击方进行监听,在反序列化漏洞位置发送payloads/JRMPClient向我们的exploit/JRMPListener进行连接,连接后会返回在exploit/JRMPListener的gadgets对象并且进行反序列化

攻击流程如下:

  1. 攻击方在自己的服务器使用exploit/JRMPListener开启一个rmi监听

  2. 往存在漏洞的服务器发送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对象的。

Java安全之ysoserial-JRMP模块分析(一)

Java安全之ysoserial-JRMP模块分析(一)

可以直接定位到getObject方法中。

getObject方法中前面第一行代码获取了外部传入进来的端口,转换成int类型。

这个比较简单,主要内容在下面这段代码中。

Java安全之ysoserial-JRMP模块分析(一)

使用Reflections.createWithConstructor方法传入三个参数获取到一个UnicastRemoteObject的实例对象。传入的参数第一个是ActivationGroupImpl.class,第二个是RemoteObject.class,而第三个则是一个Object的数组,数组中里面是RemoteRef.class,第四个是UnicastServerRef传入了刚刚获取的端口的一个实例对象。

第一个参数使用的是 ActivationGroupImpl 是因为在利用的时候,本身就是利用的 UnicastRemoteObject 的 readObject 函数,第二个参数需要满足两个条件:

  1. 要为 UnicastRemoteObject 的父类

  2. 不能在创建的过程中有其他什么多余的操作,满足这两个条件的两个类是:RemoteObject、RemoteServer

最后具体是怎么获取到的UnicastRemoteObject实例对象,这里需要调试跟踪一下。

UnicastServerRef分析

在此之前,先来看看new UnicastServerRef(jrmpPort)的内部实现。先跟踪最里层的方法。

Java安全之ysoserial-JRMP模块分析(一)

UnicastServerRef的构造方法,内部会去再new一个LiveRef对象并且传入输入进来的端口的参数。

选择跟踪。

Java安全之ysoserial-JRMP模块分析(一)

内部是new了一个ObjID,继续跟踪。

Java安全之ysoserial-JRMP模块分析(一)

里面还会去new一个UID赋值给space成员变量,UID这里自然都知道是啥意思,这里就不跟了,而下面随机获取一个值赋值给objNum。

ObjID

  • ObjID用于标识导出到RMI运行时的远程对象。 导出远程对象时,将根据用于导出的API来隐式或明确地分配一个对象标识符。

  • 构造方法:

    ObjID() 
    生成唯一的对象标识符。 
    ObjID(int objNum) 
    创建一个“众所周知”的对象标识符。 
    

执行完成后返回到这一步。

Java安全之ysoserial-JRMP模块分析(一)

这里调用了构造方法的重载方法。选择跟踪一下。

Java安全之ysoserial-JRMP模块分析(一)

到了这一步,var1的参数自然不用解释,而后面的则是传入的端口。

里面再一次调用重载方法,并且在传递的第二个参数调用了TCPEndpoint.getLocalEndpoint并且传入端口进行获取实例化对象。继续跟踪。

Java安全之ysoserial-JRMP模块分析(一)

内部调用getLocalEndpoint重载方法,跟踪。

getLocalEndpoint方法说明:

获取指定端口上本地地址空间的终结点。如果端口号为0,则返回共享的默认端点对象,其主机名和端口可能已确定,也可能尚未确定。

内部调用localEndpoints.get方法并且传入var5,也就是TCPEndpoint的实例对象。

Java安全之ysoserial-JRMP模块分析(一)

Java安全之ysoserial-JRMP模块分析(一)

localEndpoints是一个map类型的类对象,这里get方法获取了var5,对应的value值,类型为LinkedList。这里获取到的是一个null。

执行到下一步

Java安全之ysoserial-JRMP模块分析(一)

调用resampleLocalHost方法获取String的值,跟踪查看实现。

Java安全之ysoserial-JRMP模块分析(一)

Java安全之ysoserial-JRMP模块分析(一)

localHost的值是通过getHostnameProperty方法进行获取的。

Java安全之ysoserial-JRMP模块分析(一)

执行完成后,返回到sun.rmi.transport.tcp#TCPEndpoint,执行到一下代码中。

Java安全之ysoserial-JRMP模块分析(一)

这里的代码比较容易理解,var为空,new一个TCPEndpoint对象,并且传入var7,var0,var1,var2。参数值是ip,端口,null,null。将该对象添加到var6里面。

后面则是对var3的对象进行赋值,ip和端口都赋值到var3的成员变量里面去。

最后就是调用localEndpoints.put(var5, var6);讲var5, var6存储到localEndpoints中。

最后进行返回var3对象。

Java安全之ysoserial-JRMP模块分析(一)

执行完成后,回到这里

Java安全之ysoserial-JRMP模块分析(一)

继续跟踪,构造方法的重载方法。

Java安全之ysoserial-JRMP模块分析(一)

这里就没啥好说的了,就是赋值。

最后返回到外面入口的地方

Java安全之ysoserial-JRMP模块分析(一)

调用了父类的构造方法

Java安全之ysoserial-JRMP模块分析(一)

到了这里其实就已经跟踪完了。

yos利用链分析

返回到这一步跟踪Reflections.createWithConstructor查看内部实现。

Java安全之ysoserial-JRMP模块分析(一)

Java安全之ysoserial-JRMP模块分析(一)

简化一下代码:

Constructor<? super T> objCons = RemoteObject.class.getDeclaredConstructor(new UnicastServerRef(jrmpPort));

其实也就是反射调用获取 RemoteObject参数为UnicastRef的构造方法。并且传递new UnicastServerRef(jrmpPort)实例化对象作为构造方法参数。

而下面的setAccessible(objCons);这个就不做分析了,分析过前面的利用链都大概清楚,这个其实就是修改暴力反射的一个方法类。

看到下面这段代码

Java安全之ysoserial-JRMP模块分析(一)

这里进行跟踪。

Java安全之ysoserial-JRMP模块分析(一)

其实借助ReflectionFactory.getReflectionFactory()工厂方法在这里就是返回了ReflectionFactory的实例对象。

跟踪newConstructorForSerialization方法

Java安全之ysoserial-JRMP模块分析(一)

这里传递的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安全之ysoserial-JRMP模块分析(一)

这里先来思考到一个问题,为什么不能使用反射直接调用呢?

其实并非所有的java类都有无参构造方法的,并且有的类的构造方法还是private的。所以这里采用这种方式进行获取。

再来看到上面的代码:

Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);

前面参数为ActivationGroupImpl.class,指定获取ActivationGroupImpl.class的 Constructor。后面的参数为反射获取RemoteObject的RemoteRef类型构造方法获取到的Constructor类。

Java安全之ysoserial-JRMP模块分析(一)

最后将参数传递进行,返回创建一个ActivationGroupImpl实例化对象。

Java安全之ysoserial-JRMP模块分析(一)

执行完成回到这个方法内,发现该地方对ActivationGroupImpl进行了向上转型为UnicastRemoteObject类型

Java安全之ysoserial-JRMP模块分析(一)

最后调用反射将UnicastRemoteObject的实例对象的port字段修改成我们设置的端口的值。

Java安全之ysoserial-JRMP模块分析(一)

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();

    }
}

这里是利用了UnicastRemoteObjectreadObject作为反序列化的入口点。

在此处下断点开始调试分析。

Java安全之ysoserial-JRMP模块分析(一)

readObject方法处调用了reexport方法,跟踪查看。

Java安全之ysoserial-JRMP模块分析(一)

csf和ssf为空,执行到这里。

Java安全之ysoserial-JRMP模块分析(一)

调用exportObject 方法并且传入this和port 这里的this,实际上是ActivationGroupImpl,因为前面进行了向上转型。跟踪exportObject

Java安全之ysoserial-JRMP模块分析(一)

这里再次调用重载方法,跟踪查看。

Java安全之ysoserial-JRMP模块分析(一)

到了这一步,调用sref.exportObject传入前面创建的实例对象。跟踪。

Java安全之ysoserial-JRMP模块分析(一)

这里下面调用this.ref,而this.ref为LiveRef对象。这一段则是调用LiveRef.exportObject。继续跟踪。

Java安全之ysoserial-JRMP模块分析(一)

this.ep为Endpoint对象,这里调用的是Endpoint.exportObject,这里的对象是怎么赋值的前面的构造分析的时候去讲过,这里不做多的赘述。

Java安全之ysoserial-JRMP模块分析(一)

调用this.transport.exportObject;继续跟踪。

Java安全之ysoserial-JRMP模块分析(一)

到了这一步就调用了this.listen()进行启动监听。

Java安全之ysoserial-JRMP模块分析(一)

参考文章

ysoserial JRMP相关模块分析(一)- payloads/JRMPListener

0x03 结尾

JRMP的这个模块第一次分析还是挺费劲的,网上的相关资料也偏少。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/119886.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 阿里云服务器开放80端口「建议收藏」

    阿里云服务器开放80端口「建议收藏」阿里云服务器开放80端口1.先登录阿里云官网https://www.aliyun.com/2.点击控制台3.点击左边的三条杆4.点击进去之后,找到云服务器ECS5.点击蓝色字体的服务器名称6.找到安全组,安全组列表,加入安全组,配置规则7.点击入方向里的手动添加8.设置如下图所示的参数,最后记得保存哦9.至此,80端口就设置好了。因为我这里设置了Nginx,所以80端口会被占用10.我们在浏览器上访问,也可以验证成功…

  • html导航栏下拉,实现代码[通俗易懂]

    html导航栏下拉,实现代码[通俗易懂]代码如下(示例):<!DOCTYPEhtml><html> <head> <metacharset=”utf-8″> <title></title> <styletype=”text/css”> *{padding:0px;margin:0px;box-sizing:border-box;} ul,ol{list-style-type:none;} a{text-decora

  • 蓝桥杯 错误票据 原创代码+思路

    蓝桥杯 错误票据 原创代码+思路

  • 计算机二级Python公共基础部分

    计算机二级Python公共基础部分计算机二级Python公共基础部分011.基本数据结构与算法1.1算法的基本概念算法是一种设计好的计算序列,用来解决一类问题。算法复杂的包括:时间复杂度和空间复杂度。时间复杂度指计算工作量,空间复杂度指内存空间的要求。综合起来:如果一个算法的执行次数是T(n),那么只保留最高次项,同时忽略最高项的系数后得到函数f(n),此时算法的时间复杂度就是O(f(n))。为了方便描述,…

  • C# 二代身份证验证 / VerifyIDCard[通俗易懂]

    C# 二代身份证验证 / VerifyIDCard[通俗易懂]由于现在社会技术的发展,需要验证身份证的合法性越发重要那么我们如何去验证一个身份证是否合法呢?我在以前的时候,特意写过一篇VB.NET查询身份证信息当然它是通过“百度身份证信息查询”的REST接口编写的一个类,当然那个时候并不考虑通过“身份证验证算法”识别身份证是否有效,注意VB.NET的主题是“查询身份证信息”而不是“验证身份证合法性”、我们以上面的“身份

  • CocoaPods的install和update卡在“Anylyzing dependencies”的问题解决方式[效率]

    CocoaPods的install和update卡在“Anylyzing dependencies”的问题解决方式[效率]

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号