为了便于测试反序列化漏洞,这里采用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服务和反序列化机制。