In July 2013, security organization Security Explorations discovered a security vulnerability in Java 7u25 by which an attacker could completely escape the Java sandbox. Oracle released a patch in update 7u40, but as Security Explorations announced earlier this year, the patch only addressed the proof of concept, and a simple code modification still exposes the vulnerability. Moreover, subsequent investigation has revealed that the vulnerability is even more severe than initially reported. After the issue became public, Oracle released a patch as part of 8u77.
The vulnerability can be found in the new reflection library, available since Java 7, and more specifically in the new MethodHandle class used for dynamically accessing and invoking methods. It is related to the way classes loaded by different ClassLoaders are treated. Understanding of the issue requires some basic knowledge ofthe way Java ClassLoaders work; since class loading is one of the least understood aspects of Java, we will proceed to provide an introduction to this concept prior to explaining the issue itself.
Java ClassLoaders
Java has the ability to load code dynamically at runtime from a variety of sources. This is accomplished through a special category of classes called ClassLoaders. A standard Java implementation might provide several ClassLoaders to load classes from a file system, a URL, or a zipped file, among others, but also provides developers the ability to create their own custom ClassLoaders to handle custom requirements. The usual way to interact with a ClassLoader is by calling its loadClass(String) method, which will accept the name of a class, and either return the associated Class object if found, or throw a ClassNotFoundException otherwise. Every class in a Java application is loaded this way by one ClassLoader or another.
Different ClassLoaders can be connected to each other to form a hierarchy by assigning a parent ClassLoader. If no parent is assigned, the parent ClassLoader is defaulted to the one that loaded this particular ClassLoader (ClassLoaders are classes themselves, and therefore need to be loaded by some ClassLoader). When a parent ClassLoader is present, the default behaviour of a ClassLoader is to try to delegate the loading of the requested class to its parent; only if the parent (or some grandparent) cannot load the class, will this ClassLoader attempt to load the requested class itself. However, creators of custom loaders are not required to enforce this default behaviour, and they could choose to implement a different one.
Whenever a Java application is started, the following ClassLoaders will come to effect, in order:
- Bootstrap ClassLoader: part of the JVM itself, and therefore specific to each JVM implementation. This ClassLoader doesn’t have a parent ClassLoader, and is used to load the core classes under the package java.lang.
- Extension ClassLoader: responsible for loading classes in extension libraries, which tend to be different on each Java installation. The Extension ClassLoader will load anything located in the path indicated by the variable java.ext.dirs.
- Application ClassLoader: responsible for loading the main class of the application, together with all the classes in the application classpath.
- Custom ClassLoader: any other ClassLoader used within the application. It’s optional, and it may not be present depending on the application.
The ability to load classes dynamically at runtime using custom ClassLoaders has created the opportunity for a number of applications that wouldn’t be possible otherwise, but unfortunately it has also created a number of security concerns, particularly around class impersonation. A developer could, in theory, create a custom ClassLoader that loads a compromising implementation of the primordial class java.lang.Object, and use this custom Object in a Java application. This would be a security concern for two reasons: first, this custom Object class would have access to all the package-visibility content of classes in the package java.lang; and second, this custom Object would be treated by the JVM as the standard Object class, and therefore as one of the trusted class that are part of the Java implementation.
In order to secure Java against these vulnerabilitie, Java classes are identified using three attributes: class name, package, and ClassLoader reference. If two different classes with the same name and package were loaded by different ClassLoaders, Java considers them not equal, and attempting to assign between them will cause a ClassCastException. This protects the environment from class impersonation.
Partial Fix and Resulting Vulnerability
The initial vulnerability as reported by Security Explorations, and categorised as CVE-2013-5838, indicated that, when invoking a method through a Method Handle, the ClassLoader of the class whose method is being invoked was not being checked, meaning an attacker could achieve class impersonation exactly in the way described above.
Code sample where the original vulnerability is shown, the ClassLoader of target class is not checked; source: Security Explorations.
Oracle provided a fix for this in September 2013 as part of Java 7u40 which consisted in a class visibility check that compared the ClassLoaders of the expected type and the incoming type in the following manner:
- If both ClassLoaders are the same, then the two types are fully compatible by definition.
- If one ClassLoader is a parent of the other one, then it is understood that the two classes were loaded through the normal hierarchy of ClassLoaders, and therefore it is safe to assume that they are equivalent.
It is in this second check where Security Explorations found that the exploit is still possible with some minor changes. First, the custom ClassLoader used to impersonate a class can be configured to have the target ClassLoader as its parent, for this is a parameter that can be set through the API:
URLClassLoader lookup_CL = URLClassLoader.newInstance(urlArray, member_CL);
Mechanism through which the custom ClassLoader is perceived as part of the hierarchy; source: Security Explorations.
Second, given that the default behaviour for ClassLoaders is to delegate loading of a class to its parent, an attacker would have to make sure that the parent ClassLoader fails to load the class, so their custom ClassLoader the one that takes charge. This has been demonstrated to be achievable by exploiting Java’s capability to load classes over the network: if the class to be impersonated is defined to reside in a URL location, the parent ClassLoader will attempt to connect to the relevant server to obtain the code for the class; at this point, a purpose-built HTTP server can return a 404 NOT FOUND error, making the parent ClassLoader fail to load the class and therefore handing control back to the custom ClassLoader.
Resulting code flow after forcing the parent ClassLoader to fail to load the class through a custom-built HTTP server; source Security Explorations.
When the issue resurfaced in March 2016, the latest available version at the time, 8u74, proved to be vulnerable, and Oracle provided a fix for it in 8u77. However, in the release notes of 8u77, the vulnerability was still being referred to as "affecting Java SE running in web browsers on desktops [and] not applicable to Java deployments, typically in servers or standalone desktop applications", while it has been proven to affect server configurations and Google App Engine for Java as well.
Correction: 29th April 2016
This article incorrectly described the vulnerability as being still available in versions 8u77, 8u91 and 8u92, however, this was fixed in 8u77. The release notes of 8u77 refer to a fix for CVE-2016-0636, which is described as "Unspecified vulnerability [...] via unknown vectors" and doesn't include any obvious references to CVE-2013-5838, the issue described in this article. However, Security Explorations indicates that CVE-2016-0636 is a CVE number for Issue 69, where Issue 69 is their own way to refer to the original CVE-2013-5838.