I was the first winner of the Java exploit competition at this event, and this blog provides an both an overview of my winning entry, and an insight into just how difficult it is to fully secure a complex system such as Java against a determined attacker.
But let’s start with the bug itself, CVE-2013-1488, which is surprisingly innocuous and one which could easily be missed during a code security review. It was an issue I found in the java.sql.DriverManager class which is a trusted part of the framework. As the documentation states this class’s purpose is for “… managing a set of JDBC drivers.” and is an extensible Java framework for accessing relational databases. Within the source code for this class a Java vulnerability hunter would be drawn to the two AccessController.doPrivileged blocks like a moth to a flame, they allow the Java libraries to temporarily elevate its privileges to perform a security critical action. The first block does not do very much (it simply gets a system property) but the second one is far more interesting:
The reason this code is interesting is because it is performing a non-trivial task during this block of elevated privileges. Even more importantly, an attacker can directly interact with this process because of the use of the ServiceLoader class which will instantiate a sequence of objects from a manifest of class names provided in the applet’s JAR file.
That in and of itself might not be massively helpful to an attacker, mainly because all objects created have to implement the java.sql.Driver interface, so we cannot just find any arbitrary class and reuse it. We could try and find an existing driver which did something bad when created with its default ‘empty’ constructor, but doesn’t sound too likely either. But looking closer at the lines which are actually doing the work, there is something else interesting going on:
Whilst it isn’t immediately obvious these lines are doing a lot more than they seem, the code is actually looping through an “iterator” which creates each object from a list of class names in turn via the reflection API Class.newInstance(). It then concatenates that object with a string and tries to log the result (ironically by default there is no log enabled and this result is thrown away). Whilst it isn’t explicit in the code, and thus might be missed unless you are paying close attention, what this is actually doing is calling the toString method on every object it creates during a privileged block. To say this is probably bad is maybe understating the issue, but what can we do with it?
Exploiting this comes down to a variation of the “Trusted Method Chain” attack. This is a technique in platforms such as Java and .NET which allows an attacker to construct a chain of method calls from parts of the trusted Java class libraries and the JVM, which result in some action which circumvents security restrictions. I can’t claim credit for the discovery of this technique, and so for a better description in the Java world I would defer to the legendary Sami Koivu here.
The reason you need to do this is because unsigned Java code running in the browser plugin is untrusted. If you try and perform a security critical action (such as opening a file) the security manager installed to protect the sandbox will notice you and stop the action. But if all the calls on the stack are trusted (i.e. from built in classes) then the manager will trust you to perform the action, leading to the potential for a sandbox escape.
So the final piece of the puzzle is how to create the RhinoScriptEngine class under a privileged context, well the answer has been staring us in the face all along: we can use our own ServiceLoader iterator combined with a Set class to create a trusted chain between the toString method and instantiation of the RhinoScriptEngine class. The reason this works is that the Set class’s toString method will iterate over the values of the iterator object, which, in turn, causes the ServiceLoader iterator to instantiate an attacker supplied list of objects. This list can then be recovered at a later time for the purposes of exploitation. Take, for example, the following class which implements the Driver interface and derives from AbstractSet but uses a “fake” iterator:
When called by the DriverManager code the order of operations is as follows:
Once the script engine object is created, we now just need a second driver which again derives from a Set class, this time we use it to call toString on our custom Error object, by adding it to the Set, and it’s “game over”:
In conclusion, to exploit that single, seemingly innocuous bug required repurposing a massive amount of unrelated code to disable the security manager and therefore break out of the sandbox. I guess that is why Oracle considering it to have a Medium Access Complexity within the CVSSv2 scoring scheme. But that is also why I think something like Java can never be secured against hostile code running within a sandboxed environment: the attacker has too much control to craft the environment to exploit the tiniest of weaknesses. The large amount of “trusted” code bundled with the JRE just acts to amplify that power.