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 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, perhaps specified in a whitelist of trusted classes. This can be done by overriding the
resolveClass() method of the
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. If the object being deserialized belongs to a class with a lax
readObject() method, this could result in remote code execution.
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
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
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.
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.
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.
CERT Vulnerability #576313 describes a family of exploitable vulnerabilities that arise from violating this rule.
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.
|Useful for developing exploits that detect violation of this rule|
It should not be difficult to write a static analysis to check for deserialization that fails to override
resolveClass() to compare against a whitelist.
|[Terse 2015]||Terse Systems, Closing the Open Door of Java Object Serialization, Nov 8, 2015|