This is part of a series of posts detailing Java Sandbox Bypasses that were disclosed between 2012-2013. You can view the other bugs by going back to the original post.
The last two vulnerabilities I wrote up ( ZDI-13-246, ZDI-13-075) involved heap spraying so were not 100% reliable. Most of my sandbox bypasses against the JVM did not use memory corruption or heap spraying so were 100% reliable. These reliable sandbox bypasses fell into two main categories:
First there were vulnerabilites that would try to create a chain from privileged code to a ‘dangerous’ function without touching any user frames. Java uses stack walking to decide whether a dangerous function (System.setSecurityManager(null)
, Runtime.execute
) is allowed to proceed so if you could create a chain then you could subvert the protection.
Second there were vulnerabilities that got access to methods in the ‘protected packages’. After getting access to these packages it is usually trivial to escalate out of the sandbox because it is assumed user code cannot access these methods. Access to these packages usually involved abusing reflection or parts of the JDK that used reflection but did not do so securely. This vulnerability which has existed at least since Java 5 is a good example of abusing reflection to access privileged packages.
This bug is interesting because there is no ZDI public disclosure for it. I suspect this is because Adam Gowdiak disclosed it to Oracle first. Looking back I also suspect I may have sniped this vulnerability from Adam Gowdiak. Gowdiak seems to have a habit of partially publicly disclosing Java bugs before they are fixed. Another bug I disclosed to ZDI, ZDI-13-079 was based on a post he made to the full disclosure mailing list and I definitely sniped this bug from him. I can’t remember the exact details about how I found this bug but I remember Gowdiak made a presentation where he said ‘com.sun.xml.internal.bind.v2.model.nav.Navigator’ was an interesting class. It is possible that I was able to reverse the underlying bug from this.
Vulnerabilies
Three vulnerabilities are used to bypass the sandbox.
- Accessing Class instances in protected packages.
- Reading fields on interfaces in protected packages.
- Getting access to
java.lang.reflect.Method
for interface methods in protected packages.
Loading Classes in Protected Packages
The JmxMBeanServer class allows you to load classes from protected packages. This isn’t possible in Java 6.
server = JmxMBeanServer.newMBeanServer("", null, null, true);
server.getMBeanInstantiator().findClass(className, (ClassLoader)null);
findClass in MBeanInstantiator ends up calling loadClass(className, null)
which will end up performing Class.forName(className)
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Reading Fields on Interfaces in Protected Packages
If you call Proxy.getProxyClass(null, new Class[]{targetClass})
then the
generated proxy class will have all the fields from the targetClass. Because
the generated proxy class is not in a protected package user code can then call
proxyClass.getFields()
which will give back the java.lang.reflect.Field
object
and because the field is public call Field#get
will succeed. The proxy class
successfully loads because it is defined the root class loader.
Getting Access to Method objects for Interface Methods in Protected Packages
This uses a similar vulnerability as above. You can think of the Proxy instance
as a machine that will convert Method objects into Method objects for a
particular interface. If you look at proxyClass.getDeclaredMethods()
for
com.sun.xml.internal.bind.v2.model.nav.Navigator
you will see something like:
public final boolean $Proxy0.isFinal(java.lang.Object)
public final boolean $Proxy0.isArray(java.lang.Object)
..
If you call $Proxy0.isFinal(java.lang.Object)
then it will convert this Method
into Navigator.isFinal(java.lang.Object)
before passing it to the
InvocationHandler
.
To access a Method
on an interface in a protected package all you have to do is
create an InvocationHandler
that will save the Method then invoke the
corresponding public method on the proxy class.
Once an attacker has access to the Method then they are free to invoke it
because the Method
is public and no more access checks are performed.
Exploit
- We use the JMX class loading vulnerability to load the class
"com.sun.xml.internal.bind.v2.model.nav.Navigator"
. - We then use the field reading vulnerability to read the
REFLECTION
field from the interface. - We then use the interface method vulnerability to read the
getDeclaredMethods(Object o)
method from theNavigator
class.
Now that we have a way of getting Methods from a protected Class
(getDeclaredMethods
) and a way of loading protected classes (JMX vulnerability)
we can easily subvert the JVM sandbox. There is probably 100 ways of doing this
because once you can execute arbitrary static methods in the protected packages
it is game over for the JVM. We will use a technique similar to the one
disclosed in ZDI-13-159 in order to disable the sandbox except we will modify
it slightly so it only uses JDK 6 classes.
- We use
com.sun.xml.internal.bind.v2.ClassFactory#create(Class)
to create asun.reflect.ReflectionFactory$GetReflectionFactoryAction
- We use
com.sun.xml.internal.ws.api.server.InstanceResolver#createSingleton
to create anInstanceResolver
object - We use
com.sun.xml.internal.ws.api.server.InstanceResolver#createInvoker
to create anInvoker
object - We use
com.sun.xml.internal.ws.api.server.Invoker#invoke
to invokeAccessController#doPrivileged
with thePrivilegedAction
in step 1 to create aReflectionFactory
object. - We invoke
sun.reflect.ReflectionFactory#newField
with parameters that correspond to theStatement#acc
field - We invoke
sun.reflect.ReflectionFactory#newFieldAccessor
with the new field object. - We create a
Statement
object that executesSystem.setSecurityManager(null)
; - We invoke
sun.reflect.FieldAccessor#set(Object, Object)
with aStatement
object we have created and aAccessControlContext
that gives us all permissions - We execute the
Statement
which disables the JVM security.
Exploit Java 6
We use the same technique as above but we use the XSLT class loading hack disclosed in ZDI-13-159 to load the classes because this works in Java 6.
Testing (Java 7)
The POC is available from Github
java -Djava.security.manager ProxyAbuse or appletviewer test.html
It will try and print the users home directory and execute an apple script that will say some stuff.
Testing (Java 6)
The POC is available from Github
java -Djava.security.manager Harness or appletviewer test.html
It will try and print the users home directory and execute an apple script that will say some stuff.
Fixes
User code probably shouldn’t be able to load Proxy Classes in the bootstrap class loader.