Remote Code Execution via Java RMI Object Deserialization

Critical Risk Deserialization & Object Security
javarmideserializationrceremote-interfacegadget-chains

What it is

RMI deserialization vulnerabilities occur when RMI methods accept arbitrary Serializable objects as parameters. Attackers can send malicious serialized objects containing gadget chains that execute arbitrary code during deserialization. RMI services are particularly vulnerable because they automatically deserialize method parameters over the network.

import java.rmi.*;
import java.io.Serializable;

// VULNERABLE: RMI interface accepts arbitrary objects
public interface DataService extends Remote {
    // DANGEROUS: accepts any Serializable object
    String processData(Serializable data) throws RemoteException;
    
    // DANGEROUS: accepts Object directly
    Object executeTask(Object task) throws RemoteException;
}

// Implementation
public class DataServiceImpl implements DataService {
    
    @Override
    public String processData(Serializable data) throws RemoteException {
        // VULNERABLE: data could be malicious gadget chain
        if (data instanceof String) {
            return "Processed: " + data;
        } else if (data instanceof UserData) {
            return processUserData((UserData) data);
        }
        return "Unknown type";
    }
    
    @Override
    public Object executeTask(Object task) throws RemoteException {
        // Extremely dangerous - accepts any object
        return "Task executed";
    }
}

// Attacker can send gadget chain payload using tools like ysoserial
import java.rmi.*;

// SECURE: RMI interface uses primitives and IDs
public interface SecureDataService extends Remote {
    // SAFE: uses primitive types and strings
    String processDataById(long dataId) throws RemoteException;
    
    // SAFE: uses JSON string instead of objects
    String submitTask(String taskType, String jsonParams) 
        throws RemoteException;
    
    // SAFE: returns status by ID
    String getTaskStatus(long taskId) throws RemoteException;
}

// Secure implementation
public class SecureDataServiceImpl implements SecureDataService {
    
    private final DataRepository repository;
    private final ObjectMapper jsonMapper;
    
    public SecureDataServiceImpl() throws RemoteException {
        this.repository = new DataRepository();
        this.jsonMapper = new ObjectMapper();
        
        // Enable JEP 290 deserialization filter
        System.setProperty("jdk.serialFilter", 
            "java.lang.String;java.lang.Number;!*");
    }
    
    @Override
    public String processDataById(long dataId) throws RemoteException {
        // Load data server-side using ID - no deserialization of user objects
        Object data = repository.findById(dataId);
        return processDataSecurely(data);
    }
    
    @Override
    public String submitTask(String taskType, String jsonParams) 
            throws RemoteException {
        // Validate task type
        if (!isValidTaskType(taskType)) {
            throw new RemoteException("Invalid task type");
        }
        
        // Safe JSON deserialization to simple DTO
        TaskParamsDTO params = jsonMapper.readValue(
            jsonParams, TaskParamsDTO.class);
        
        return taskService.createTask(taskType, params);
    }
    
    @Override
    public String getTaskStatus(long taskId) throws RemoteException {
        return taskService.getStatus(taskId).toString();
    }
}

💡 Why This Fix Works

The vulnerable code accepts Serializable and Object parameters in RMI methods, allowing attackers to send malicious gadget chains that execute code during deserialization. The secure version uses only primitive types, strings, and IDs as parameters, loading data server-side and using JSON for complex data transfer.

Why it happens

Defining RMI interface methods that accept Serializable or Object parameters.

Root causes

RMI Methods Accepting Serializable Objects

Defining RMI interface methods that accept Serializable or Object parameters.

Missing Deserialization Filters

Not implementing ObjectInputFilter to restrict which classes can be deserialized.

Complex Object Parameters

Using complex objects instead of primitives and IDs for RMI method parameters.

Fixes

1

Use Primitives and IDs

Replace object parameters with primitive types, strings, and IDs. Load data server-side.

2

Enable JEP 290 Filters

Configure ObjectInputFilter with restrictive allowlists of permitted classes.

3

Use Alternative RPC

Consider using gRPC, REST APIs, or other RPC frameworks that don't use Java serialization.

Detect This Vulnerability in Your Code

Sourcery automatically identifies remote code execution via java rmi object deserialization and many other security issues in your codebase.