为了便于测试反序列化漏洞,这里采用RMI服务作为示例,演示如何攻击一个RMI后端服务,因为RMI的通讯方式就是对象序列化/反序列化。
添加commons-collections依赖:
<dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.1</version></dependency>
RMITestInterface.java
import java.rmi.Remote;import java.rmi.RemoteException;public interface RMITestInterface extends Remote {String test() throws RemoteException;}
RMITestImpl.java
import java.rmi.RemoteException;import java.rmi.server.UnicastRemoteObject;public class RMITestImpl extends UnicastRemoteObject implements RMITestInterface {private static final long serialVersionUID = 1L;protected RMITestImpl() throws RemoteException {super();}public String test() throws RemoteException {return "Hello RMI~";}}
RMI服务端:
import java.rmi.Naming;import java.rmi.registry.LocateRegistry;public class RMIServerTest {// RMI服务器IP地址public static final String RMI_HOST = "127.0.0.1";// RMI服务端口public static final int RMI_PORT = 9527;// RMI服务名称public static final String RMI_NAME = "rmi://" + RMI_HOST + ":" + RMI_PORT + "/test";public static void main(String[] args) {try {// 注册RMI端口LocateRegistry.createRegistry(RMI_PORT);// 绑定Remote对象Naming.bind(RMI_NAME, new RMITestImpl());System.out.println("RMI服务启动成功,服务地址:" + RMI_NAME);} catch (Exception e) {e.printStackTrace();}}}
程序运行结果:RMI服务启动成功,服务地址:rmi://127.0.0.1:9527/test
RMI客户端:
package com.anbai.sec.rmi;import java.rmi.Naming;public class RMIClientTest {public static void main(String[] args) {try {// 查找远程RMI服务RMITestInterface rt = (RMITestInterface) Naming.lookup("rmi://127.0.0.1:9527/test");// 调用远程接口RMITestInterface类的test方法String result = rt.test();// 输出RMI方法调用结果System.out.println(result);} catch (Exception e) {e.printStackTrace();}}}
程序运行结果:Hello RMI~
上述示例演示了一个业务逻辑绝对的简单且安全RMI服务的正常业务流程,但是漏洞并不是出现在业务本身,而是Java的RMI服务和反序列化机制。
