This project demonstrates a critical security vulnerability in Java applications: Insecure Deserialization. This vulnerability allows attackers to execute arbitrary code on the server by sending specially crafted serialized objects.
This is a demonstration application with intentionally vulnerable code.
- Only run this in a controlled, isolated environment
- Never deploy this code to production
- The application contains code that can execute arbitrary commands
- Use only for educational and testing purposes
Insecure deserialization occurs when an application deserializes untrusted data without proper validation. In Java, this can lead to:
- Remote Code Execution (RCE) - Attackers can execute arbitrary commands on the server
- Privilege Escalation - Gain elevated permissions
- Denial of Service (DoS) - Crash the application
- Data Tampering - Modify application state
The application contains a VulnerableClass that implements Serializable:
public class VulnerableClass implements Serializable {
private String data;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
if (data != null && data.startsWith("EXEC:")) {
String command = data.substring(5);
Runtime.getRuntime().exec(command);
}
}
}- The
readObject()method is called automatically during deserialization - It checks if the data starts with "EXEC:" and executes the rest as a command
- No validation is performed on the input
- The application deserializes any object sent to it
An attacker can:
- Create a
VulnerableClassinstance with data like "EXEC:calc" - Serialize it to bytes
- Base64 encode the bytes
- Send it to the
/api/deserializeendpoint - The server deserializes it and executes the command
- Java 11 or higher
- Maven 3.6 or higher
# Build the project
mvn clean compile
# Run the Spring Boot application
mvn spring-boot:runThe application will start on http://localhost:8080
- Open
http://localhost:8080in your browser - Test with safe payloads first
- Try the exploit demonstration (be careful!)
Here's how an attacker could exploit this vulnerability:
// Create malicious object
VulnerableClass exploit = new VulnerableClass("EXEC:calc");
// Serialize it
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(exploit);
oos.close();
// Base64 encode
String payload = Base64.getEncoder().encodeToString(bos.toByteArray());
// Send to vulnerable endpoint
// POST /api/deserialize with payload as bodyThis vulnerability can be exploited in various scenarios:
- Web Applications - User input is deserialized without validation
- RPC/Remoting - Remote method calls with serialized parameters
- Caching Systems - Deserializing cached objects
- Message Queues - Processing serialized messages
- Session Storage - Deserializing session data
Best Practice: Don't use Java's built-in serialization for untrusted data.
// Instead of ObjectInputStream, use safer alternatives
// JSON, XML, Protocol Buffers, etc.public class SecureDeserializer {
private static final Set<String> ALLOWED_CLASSES = Set.of(
"com.example.SafeClass",
"java.util.ArrayList"
);
public Object deserialize(byte[] data) throws Exception {
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data))) {
// Override resolveClass to check allowed classes
ObjectInputStream secureOis = new ObjectInputStream(new ByteArrayInputStream(data)) {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String className = desc.getName();
if (!ALLOWED_CLASSES.contains(className)) {
throw new SecurityException("Class not allowed: " + className);
}
return super.resolveClass(desc);
}
};
return secureOis.readObject();
}
}
}public class SafeDataTransfer {
private String data;
// Use JSON instead of Java serialization
public String toJson() {
return new ObjectMapper().writeValueAsString(this);
}
public static SafeDataTransfer fromJson(String json) {
return new ObjectMapper().readValue(json, SafeDataTransfer.class);
}
}public ResponseEntity<String> deserializeObject(@RequestBody String base64Data) {
// Validate input size
if (base64Data.length() > 10000) {
return ResponseEntity.badRequest().body("Payload too large");
}
// Validate base64 format
if (!base64Data.matches("^[A-Za-z0-9+/]*={0,2}$")) {
return ResponseEntity.badRequest().body("Invalid base64 format");
}
// Additional validation...
}// Enable security manager
System.setSecurityManager(new SecurityManager());
// Configure security policies
// -java.security.policy=security.policyAdd security headers to prevent other attacks:
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.headers()
.frameOptions().deny()
.contentTypeOptions()
.and()
.httpStrictTransportSecurity()
.and()
.csrf().disable(); // For demo purposes only
return http.build();
}
}- Create serialized objects with different payloads
- Test with malicious gadgets (Commons-Collections, etc.)
- Use tools like ysoserial to generate payloads
@Test
public void testDeserializationVulnerability() {
// Test with known malicious payloads
String maliciousPayload = "rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAAA==";
// This should be rejected or handled safely
ResponseEntity<String> response = restTemplate.postForEntity(
"/api/deserialize",
maliciousPayload,
String.class
);
// Verify the response is safe
assertThat(response.getStatusCode()).isNotEqualTo(HttpStatus.OK);
}This project is for educational purposes only. The authors are not responsible for any misuse of this code. Always follow security best practices in production environments.
