Remote Code Execution via Unsafe Java Object Deserialization

Critical Risk Deserialization & Object Security
javadeserializationrceobjectinputstreamgadget-chains

What it is

Unsafe deserialization vulnerabilities occur when ObjectInputStream.readObject() processes untrusted data without filtering or validation. Attackers can craft malicious serialized objects containing gadget chains from libraries in the classpath that execute arbitrary code during deserialization, leading to complete system compromise.

import java.io.*;
import javax.servlet.http.*;

@RestController
public class DataController {
    
    @PostMapping("/api/data/import")
    public String importData(@RequestBody byte[] data) {
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(data);
            ObjectInputStream ois = new ObjectInputStream(bis);
            
            // DANGEROUS: deserializes untrusted data
            Object obj = ois.readObject();
            
            return "Data imported";
        } catch (Exception e) {
            return "Import failed";
        }
    }
}

// Attacker sends malicious serialized payload with gadget chain
// Payload executes arbitrary code during readObject()
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.http.*;

@RestController
public class SecureDataController {
    
    private final ObjectMapper jsonMapper = new ObjectMapper();
    
    @PostMapping("/api/data/import")
    public String importData(@RequestBody String jsonData) {
        try {
            // SAFE: JSON deserialization to known DTO
            UserDataDTO userData = jsonMapper.readValue(
                jsonData, UserDataDTO.class);
            
            // Validate before processing
            if (userData.isValid()) {
                processData(userData);
                return "Data imported";
            }
            return "Invalid data";
        } catch (Exception e) {
            return "Import failed";
        }
    }
}

💡 Why This Fix Works

The vulnerable code uses ObjectInputStream.readObject() on untrusted data, allowing attackers to execute arbitrary code via gadget chains. The secure version uses JSON deserialization which only constructs simple data objects without executing code, and validates the data before processing.

Why it happens

Using ObjectInputStream.readObject() on data from HTTP requests, files, or network sockets.

Root causes

Deserializing Untrusted Data

Using ObjectInputStream.readObject() on data from HTTP requests, files, or network sockets.

Missing ObjectInputFilter

Not implementing ObjectInputFilter to restrict which classes can be deserialized.

Gadget Chain Libraries

Having libraries like Commons Collections or Spring in classpath enabling gadget chains.

Fixes

1

Use JSON Instead

Replace Java serialization with JSON, which doesn't execute code during parsing.

2

Implement ObjectInputFilter

Use ObjectInputFilter with strict allowlists to control deserialization.

3

Authenticate Serialized Data

Use HMAC to verify data integrity before deserialization.

Detect This Vulnerability in Your Code

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