CVE-2013-0422 – aka: Java 2013 0day 1 – and inofficial patch

by Michael 'mihi' Schierl, @mihi42

Update 2012-01-13 22:30UTC

Oracle has released a patch now. Therefore, my unofficial patch is no longer available for download.

Analysis

Not by me. At least not now. There is some good analysis available at Immunity, although I do not 100% agree to all of the conclusions drawn.

Fixing

For issue 1, it is enough to add a checkPackageAccess call into MBeanInstantiator class. As there is a second place that instantiates classes (there is a third one in deserialize, but I am pretty confident classes created there cannot "leak"), I added a second call:

--- com/sun/jmx/mbeanserver/MBeanInstantiator.java.orig	Sat Jan 12 19:57:00 2013
+++ com/sun/jmx/mbeanserver/MBeanInstantiator.java	Sat Jan 12 20:10:20 2013
@@ -165,6 +165,7 @@
                 // Ok we do not have a primitive type ! We need to build
                 // the signature of the method
                 //
+                ReflectUtil.checkPackageAccess(signature[i]);
                 if (aLoader != null) {
                     // We need to load the class through the class
                     // loader of the target object.
@@ -626,6 +627,7 @@
                 IllegalArgumentException("The class name cannot be null"),
                               "Exception occurred during object instantiation");
         }
+        ReflectUtil.checkPackageAccess(className);
         try {
             if (loader == null)
                 loader = MBeanInstantiator.class.getClassLoader();

Issue 2 is a bit different. Here I disagree with the Immunity opinion that the bug is in missing skipping of reflection classes. Reason is that the "new" reflection mechanism should by design not leave any trace in the stack trace, as it patches up the caller stack so that it looks as if the reflectively loaded method is called directly (and patching the CallSite so that if the same method is called again at the same place, it is much faster. Also speeding up the return case as fewer frames have to be popped). Apparently, this stack trace cleaning sometimes forgets a frame or two, so that issue should be fixed. But since this is in native code (as well as the stack walk to find the caller), it is not that easy to fix. Maybe for the same reason, MethodHandleNatives#isCallerSensitive was introduced in Java 7u9. Methods listed there will perform some more complex and slower caller binding to (hopefully) avoid this issue. However, all the Lookup.find* methods were not included here. So I patched it to include all the Lookup methods, and also include all the MethodHandles methods since the comment in getCallerClassAtEntryPoint suggests that they use the same mechanism:

*** java/lang/invoke/MethodHandleNatives.java.orig	Wed Nov 28 04:42:18 2012
--- java/lang/invoke/MethodHandleNatives.java	Sat Jan 12 19:55:20 2013
***************
*** 411,416 ****
--- 411,418 ----
      static boolean isCallerSensitive(MemberName mem) {
          assert(mem.isInvocable());
          Class<?> defc = mem.getDeclaringClass();
+         if (defc == MethodHandles.class || defc == Lookup.class)
+         	return true; // better safe than sorry
          switch (mem.getName()) {
          case "doPrivileged":
              return defc == java.security.AccessController.class;

How to use the fix

Most people do not like to recompile their JDK (or rebuild the rt.jar and the shared classes cache), but fortunately there is a (not very well documented and surely not intended for this use case) feature that if you have a jar file in jre/lib/endorsed folder, all class files in there will shadow class files of rt.jar. It was originally intended for updating XML parsers in Java 1.4 where it might have been needed more urgently than now, but it still works in Java 7.

Note that this patch will only work with Java 7 Update 10 (not Update 9 or any other Java version) and I won't take any responsibilities that it fixes the bug completely or that it won't break any other Java applications. I tested it with some exploits and it seemed to block them fine, though, and Eclipse still runs when using it :-)

Download Java7ZeroDay2013Buster1.jar (38 KB) No longer available, see above

Readme:

Java 7 2013 Zero Day 1 Buster

by Michael 'mihi' Schierl, <schierlm at gmx.de>, http://schierlm.users.sourceforge.net/

To use, locate the (jre/)lib/security folder in your JDK/JRE (there should be a
file called cacerts in it), create a folder (jre/)lib/endorsed next to it and
place this Jar inside it. The Java VM will load all Jar files in this folder
and replace any of its own runtime classes (from rt.jar) by .class files inside
of these Jars. Note that this feature is not officially supported by Sun/Oracle
except for updating XML parser libraries, but it seems to work.

Use this Jar only for Java 7 Update 10, as other versions may have a
different version of the patched class and break horribly.

The patch seems to properly block the access vectors used by the 0day circulating
at the moment, but I take no responsibility that it fixes all ways this bug can be
exploited, nor that it will not break any other existing Java programs.

The source code of the patched class is also included in the Jar file.

Stay safe!