Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Parasoft Jtest 2022.2

Deserializing untrusted data can cause Java to create an object of an arbitrary attacker-specified class, provided that the class is available on the classpath specified for the JVM.  Some classes have triggers that execute additional code when they are created in this manner; see SEC58-J. Deserialization methods should not perform potentially dangerous operations for more information.  If such classes are poorly designed, such code could even call invoke arbitrary methods, such as Runtime.exec() with an attacker-supplied argument.  Therefore, untrusted input to be deserialized should be first validated to ensure that the serialized data contains only trusted classes from , perhaps specified in a whitelist of expected trusted classes.  This can be done by overloading overriding the resolveClass() method of the java.io.ObjectInputStream class.  As an alternative to validation of the serialized data, a SecurityManager can be used to perform deserialization in a less-privileged context

This rule applies only to untrusted data. Data that does not cross a program's trust boundary is, by definition, trusted and can be deserialized without violating this rule. See the exception SER12-EX0 for more information.

Deserialization of data that is trusted but must cross a trust boundary (perhaps because it originates from a different host) automatically complies with this rule, but must also comply with SER02-J. Sign then seal objects before sending them outside a trust boundary for more information.

Non-Compliant Code Example

This non-compliant code deserializes a byte array without first validating what classes will be created and without using a SecurityManager. If the object being deserialized belongs to a class with a lax readObject() method, this could result in remote code execution.

Code Block
bgColor#FFcccc
languagejava
import java.io.*;

class DeserializeExample {
    public static Object deserialize(byte[] buffer) throws IOException, ClassNotFoundException {
        Object ret = null;
        try (ByteArrayInputStream bais = new ByteArrayInputStream(buffer)) {
            try (ObjectInputStream ois = new ObjectInputStream(bais)) {
                ret = ois.readObject();
            }
        }
        return ret;
    }
} 

...

Compliant Solution (Look-Ahead Java Deserialization)

This compliant solution is based on http://www.ibm.com/developerworks/library/se-lookahead/ :. It inspects the class of any object being deserialized, before its readObject() method is invoked. The code consequently throws an InvalidClassException unless the class of the object (and of all sub-objects) is either a GoodClass1 or a GoodClass2

Code Block
bgColor#ccccff
languagejava
import java.io.*;
import java.util.*;

class LookAheadObjectInputStreamWhitelistedObjectInputStream extends ObjectInputStream {
  public Set whitelist;

  public LookAheadObjectInputStreamWhitelistedObjectInputStream(InputStream inputStream, Set wl) throws IOException {
        super(inputStream);
    whitelist = wl;
  }
  
  @Override
    protected Class<?> resolveClass(ObjectStreamClass desccls) throws IOException, ClassNotFoundException {
        switch (descif (!whitelist.contains(cls.getName())) {
            case "GoodClass1": break;
            case "GoodClass2": break;
            default:	
                throw new InvalidClassException("Unexpected serialized class", desccls.getName());
        }
        return super.resolveClass(desccls);
    }
}
 
class DeserializeExample {
    private static Object deserialize(byte[] buffer) throws IOException, ClassNotFoundException {
        Object ret = null;
    Set whitelist = new HashSet<String>(Arrays.asList(new String[]{"GoodClass1","GoodClass2"}));
    try (ByteArrayInputStream bais = new ByteArrayInputStream(buffer)) {
            try (LookAheadObjectInputStreamWhitelistedObjectInputStream ois = new LookAheadObjectInputStreamWhitelistedObjectInputStream(bais, whitelist)) {
                ret = ois.readObject();
            }
        }
        return ret;
    } 
}
}

It might appear that the above compliant solution violates Rule OBJ09-J. Compare classes and not class names.  However, the security issue addressed by that rule is applicable only when comparing the class of an object that might have been loaded by a foreign ClassLoader, i.e., an object x for which it cannot be guaranteed that x.getClass()==Class.forName(x.getClass().getName()).  In WhitelistedObjectInputStream.resolveClass() (which is the method that does the comparison of class names), a check could be added to verify that the return value (let's call it "ret") is such that ret == Class.forName(ret.getName()), but this check would always succeed, so it is pointless to add.

On a somewhat related point, it may be noted that ObjectInputStream.resolveClass() compares the serialVersionUID from the serialized data to the serialVersionUID of the Class object that it is going to return; if there is a mismatch, it throws an exception.

Whitelist

The construction of a suitable whitelist is itself a real challenge. Remote-code-execution exploits have been constructed, not only for classes in the Apache Commons Collection, but also for such core classes as java.util.concurrent.AtomicReferenceArray (CVE 2012-0507), and even simple classes like java.util.HashSet [Terse 2015] and java.net.URL [Terse 2015] can cause a JVM to hang or exhaust memory.

Exceptions

SER12-EX0: Serialized data from a trusted input source does not require sanitization, provided that the code clearly documents that it relies on the input source being trustworthy.  For example, if a library is being audited, a routine of that library may have a documented precondition that its callers pre-sanitize any passed-in serialized data or confirm the input source as trustworthy.

Related Vulnerabilities

CERT Vulnerability #576313 describes a family of exploitable vulnerabilities that arise from violating this rule.

Risk Assessment

Whether a violation of this rule is exploitable depends on what classes are on the JVM's classpath.  (Note that this is a property of the execution environment, not of the code being audited.) In the worst case, it could lead to remote execution of arbitrary code.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

SER12-J

High

LikelyHighP9L2

Automated Detection

...

Tool
Version
Checker
Description
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

JAVA.CLASS.SER.ND

Serialization Not Disabled (Java)

Parasoft Jtest
Include Page
Parasoft_V
Parasoft_V
CERT.SER12.VOBDValidate objects before deserialization

ysoserial



Useful for developing exploits that detect violation of this rule

It should not be difficult No known tools for automated detection exist yet.  However, it should not to be hard to write a static analysis to check for deserialization that fails to overload override resolveClass() to compare against a whitelist.

 Related Guidelines

MITRE CWE

CWE-502, Deserialization of Untrusted Data

Bibliography


Image Added Image Added Image Added