(This post brought to you by Esteban, Immunity's in-house Java friend)
Summary
A couple of days ago, a Java 0day was found running like crazy in the wild. While a lot of defense bunnies where asking "WWMAD" (What will my Antivirus do?), we decide to dive into Java for the details of the vulnerability and as we expected, the unpatched vulnerabilities used in the Gondvv exploit were more than one (When we said, "dive deep into Java", we actually meant open our new
Infiltrate 2013 Master Class slide deck which will include a full day of Java auditing).
The first bug was used to get a reference to sun.awt.SunToolkit class that is restricted to applets while the second bug invokes the getField public static method on SunToolkit using reflection with a trusted immediate caller bypassing a security check.
The beauty of this bug class is that it provides 100% reliability and is multiplatform. Hence this will shortly become the penetration test Swiss knife for the next couple of years (as did its older brother CVE-2008-5353).
As a final note, the bug was introduced in Java 7.0 released in July 28, 2011. While you are feeling the rush of blood going through your veins while by getting all those shell being pop, think that somewhere not far way (Probably a 10hs flight from some of the major airports in Norte Americana) was enjoying it non-stop for quite some time now.
Introduction
As the “Secure Coding Guidelines” document [1] states, it is
important to understand how the permissions are checked in the Java
security model. (Please read
guideline 9-1).
Many operations in the JDK perform permission checks before
executing. Whenever a call to
java.security.AccessController.checkPermission method
is performed, the complete call stack that exists at that moment is
analyzed.
If any of the callers in the stack do not have the required privileged an exception is raised.
When we are running code in an Applet
in our browser, there is a context that has very restricted
permissions.
This means that if there is any caller
in the stack that is part of our applet, the permission checks will
fail (unless there is a doPrivileged code block, but let's leave that
out for now).
Section “9 – Access Control” in
the “Secure Coding Guidelines” document [1] together with the
“Java Security Architecture” document [2] will give you a
complete insight on how all this works.
The Gondvv exploit
A PoC for this 0 day exploit
quickly began to spread when Joshua J. Drake posted it on Twitter
https://twitter.com/jduck1337/status/239875285913317376.
By analyzing this implementation we can
clearly see how the exploitation is done and where the vulnerabilities are really located.
The first thing we notice, is that
most of the online analysis talks about one vulnerability where we saw
two 2 vulnerabilities being exploited to achieve full execution on a
target.
Basically the exploit is creating
an
java.security.AccessControlContext
instance with a
java.security.ProtectionDomain
that has full permissions and then replace the actual
AccessControlContext of a
java.beans.Statement
instance to be able to execute code with full privileges.
So let's take a better look at each
part to understand what is happening under the hood.
In the
java.beans.Statement
implementation we can see that the AccessControlContext instance is a
private final field and it gets its value by calling
AccessController.getContext().
public
class
Statement {
private
static
Object[] emptyArray
= new
Object[]{};
static
ExceptionListener defaultExceptionListener
= new
ExceptionListener() {
public
void
exceptionThrown(Exception e) {
System.err.println(e);
System.err.println("Continuing
...");
}
};
private
final
AccessControlContext acc
= AccessController.getContext();
private
final
Object target;
private
final
String methodName;
private
final
Object[] arguments;
ClassLoader
loader;
[...]
}
|
That call to the getContext
method will set the AccessControlContext to an applet context that
has restrictions with a limited ProtectionDomain which of course is
not privileged at all.
Back in 2010 Sami Koivu published
information about a Java vulnerability (CVE-2010-0840) that built a
“trusted method chain” [4].
You can see in the article that
the exploitation of that vulnerability also made use of the
java.beans.Statement class.
The article also explains that the fix
was to add an AccessControlContext field to the Statement class
setting its value to the applet context when creating an instance
thus avoiding the full trusted chain.
This AccessControllContext field added
in that fix is exactly what this new 0 day exploit is replacing in
order to be able to execute code with full permissions.
But how can a private field be
changed?
The trick here is to use the
sun.awt.SunToolkit class
which contains a very interesting public static method:
public
static
Field getField(final
Class klass, final
String fieldName) {
return
AccessController.doPrivileged(new
PrivilegedAction<Field>() {
public
Field run() {
try
{
Field
field = klass.getDeclaredField(fieldName);
assert
(field != null);
field.setAccessible(true);
return
field;
}
catch
(SecurityException e) {
assert
false;
}
catch
(NoSuchFieldException e) {
assert
false;
}
return
null;
}
});
}
|
We can see that it is using reflection
to get fields and the complete implementation is inside a
doPrivileged block. This
getField method can be used to get
any field on a class and the good thing is that it can even retrieve
private ones.
Well this is of course very useful,
it's important to notice that the classes that are part of certain
packages are restricted for applets and cannot be accessed or used.
Such packages are:
- com.sun.deploy.*
- com.sun.imageio.*
- com.sun.javaws.*
- com.sun.jnlp.*
- com.sun.xml.internal.bind.*
- com.sun.xml.internal.ws.*
- sun.*
Trying to instantiate or use classes in
these packages will result in an AccessControlException.
This means that we are not able to get
a reference to the
sun.awt.SunToolkit class from our applet.
But the basic
thing we need to know is that calls
to certain methods can potentially bypass the
SecurityManager checks depending on the immediate caller's class
loader.
This is explained in detail in the “Secure Coding Guidelines”
document by Sun [1] in guidelines 9-8 and 9-9.
This exploit is abusing this situation taking advantage of the
immediate caller to bypass security checks.
Vulnerabilities
There
are 2 different zero-day vulnerabilities used in this exploit:
one is used to obtain a reference to the sun.awt.SunToolkit
class
and the other is used to invoke the public getField
method on that class.
The exploit is
making use of the java.beans.Expression which is a
java.beans.Statement subclass.
There are 2
Expression instances that are used to trigger these 2
different bugs.
When the
Expression.execute method is called it ends up calling
Statement.invokeInternal method, so let's check the
implementation:
private
Object invokeInternal() throws
Exception {
Object
target = getTarget();
String
methodName = getMethodName();
if
(target == null
|| methodName == null)
{
throw
new
NullPointerException((target == null
? "target"
:
"methodName")
+ " should not be null");
}
Object[]
arguments = getArguments();
if
(arguments == null)
{
arguments
= emptyArray;
}
//
Class.forName() won't load classes outside
//
of core from a class inside core. Special
//
case this method.
if
(target == Class.class
&& methodName.equals("forName"))
{
return
ClassFinder.resolveClass((String)arguments[0],
this.loader);
}
Class[]
argClasses = new
Class[arguments.length];
for(int
i = 0; i < arguments.length;
i++) {
argClasses[i]
= (arguments[i] == null)
? null
: arguments[i].getClass();
}
AccessibleObject
m = null;
if
(target instanceof
Class) {
/*
For
class methods, simluate the effect of a meta class
by
taking the union of the static methods of the
actual
class, with the instance methods of "Class.class"
and
the overloaded "newInstance" methods defined by the
constructors.
This
way "System.class", for example, will perform both
the
static method getProperties() and the instance method
getSuperclass()
defined in "Class.class".
*/
if
(methodName.equals("new"))
{
methodName
= "newInstance";
}
//
Provide a short form for array instantiation by faking an
nary-constructor.
if
(methodName.equals("newInstance")
&& ((Class)target).isArray()) {
Object
result = Array.newInstance(((Class)target).getComponentType(),
arguments.length);
for(int
i = 0; i < arguments.length;
i++) {
Array.set(result,
i, arguments[i]);
}
return
result;
}
if
(methodName.equals("newInstance")
&& arguments.length
!= 0) {
//
The Character class, as of 1.4, does not have a constructor
//
which takes a String. All of the other "wrapper" classes
//
for Java's primitive types have a String constructor so we
//
fake such a constructor here so that this special case can be
//
ignored elsewhere.
if
(target == Character.class
&& arguments.length
== 1 &&
argClasses[0]
== String.class)
{
return
new
Character(((String)arguments[0]).charAt(0));
}
try
{
m
= ConstructorFinder.findConstructor((Class)target,
argClasses);
}
catch
(NoSuchMethodException exception) {
m
= null;
}
}
if
(m == null
&& target != Class.class)
{
m
= getMethod((Class)target,
methodName, argClasses);
}
if
(m == null)
{
m
= getMethod(Class.class,
methodName, argClasses);
}
}
else
{
/*
This
special casing of arrays is not necessary, but makes files
involving
arrays much shorter and simplifies the archiving infrastrcure.
The
Array.set() method introduces an unusual idea - that of a static
method
changing
the state of an instance. Normally statements with side
effects
on objects are instance methods of the objects themselves
and
we reinstate this rule (perhaps temporarily) by special-casing
arrays.
*/
if
(target.getClass().isArray() &&
(methodName.equals("set")
|| methodName.equals("get")))
{
int
index = ((Integer)arguments[0]).intValue();
if
(methodName.equals("get"))
{
return
Array.get(target,
index);
}
else
{
Array.set(target,
index, arguments[1]);
return
null;
}
}
m
= getMethod(target.getClass(), methodName, argClasses);
}
if
(m != null)
{
try
{
if
(m instanceof
Method) {
return
MethodUtil.invoke((Method)m,
target, arguments);
}
else
{
return
((Constructor)m).newInstance(arguments);
}
}
catch
(IllegalAccessException iae) {
throw
new
Exception("Statement cannot
invoke: " +
methodName
+ " on "
+ target.getClass(),
iae);
}
catch
(InvocationTargetException ite) {
Throwable
te = ite.getTargetException();
if
(te instanceof
Exception) {
throw
(Exception)te;
}
else
{
throw
ite;
}
}
}
throw
new
NoSuchMethodException(toString());
}
|
And the
Statement.getMethod implementation is:
static
Method getMethod(Class<?> type, String name, Class<?>...
args) {
try
{
return
MethodFinder.findMethod(type,
name, args);
}
catch
(NoSuchMethodException exception) {
return
null;
}
}
|
Highlighted in the code you'll see the calls to
com.sun.beans.finder.ClassFinder.resolveClass and
com.sun.beans.finder.MethodFinder.findMethod methods.
com.sun.beans.finder.ClassFinder.findClass
vulnerability
The
Statement.invokeInternal method
is calling
com.sun.beans.finder.ClassFinder.resolveClass
and if we take a look a its
implementation we'll see that it ends up calling the
com.sun.beans.finder.ClassFinder.findClass method:
public
static
Class<?> resolveClass(String name, ClassLoader loader)
throws
ClassNotFoundException {
Class<?>
type = PrimitiveTypeMap.getType(name);
return
(type == null)
? findClass(name,
loader): type;
}
|
public
static Class<?>
findClass(String name) throws
ClassNotFoundException {
try
{
ClassLoader
loader = Thread.currentThread().getContextClassLoader();
if
(loader == null)
{
loader
= ClassLoader.getSystemClassLoader();
}
if
(loader != null)
{
return
Class.forName(name,
false,
loader);
}
} catch
(ClassNotFoundException exception) {
//
use current class loader instead
} catch
(SecurityException exception) {
//
use current class loader instead
}
return
Class.forName(name);
}
|
This code shows that if an exception is
captured, then the default is to simply call Class.forName and this
is exaclty what happens here.
As it is explained in the Guideline
9-9: “Safely invoke standard APIs that perform tasks using the
immediate caller's class loader instance” on the “Secure Code
Guidelines” documentation [1], a call to
Class.forName will
use the immediate caller ClassLoader and in this case the caller is
part of the JDK which is trusted thus allowing us to get any class on
any package.
The caller's stack can be seen by simply
debugging the applet:
MethodFinder.findMethod vulnerability
According to the “Secure Code Guidelines” document in
guideline 9.8 the
java.lang.Class.getMethod and
java.lang.Class.getMethods only
take the immediate caller into account when performing security
checks.
These methods can
be used to get a Method reference via reflection but only “public”
ones.
Even though we
have a reference to the
sun.awt.SunToolkit class we cannot
call any of its methods directly because is part of a restricted
package and a security exception will be raised.
What is needed
here is a way of getting a method reference via reflection but having
a “trusted” immediate caller in the stack in order to bypass
security checks.
The implementation
of
com.sun.beans.finder.MethodFinder.findMethod is this:
public
static
Method findMethod(Class<?> type, String name,
Class<?>...args) throws
NoSuchMethodException {
if
(name == null)
{
throw
new
IllegalArgumentException("Method
name is not set
}
PrimitiveWrapperMap.replacePrimitivesWithWrappers(args);
Signature
signature = new
Signature(type, name, args);
Method
method = CACHE.get(signature);
if
(method != null)
{
return
method;
}
method
= findAccessibleMethod(new
MethodFinder(name, args).find(type.getMethods()));
CACHE.put(signature,
method);
return
method;
} |
The call to
findAccessibleMethod ends up calling
java.lang.Class.getMethods and the immediate caller in the
stack is
com.sun.beans.finder.MethodFinder which is trusted
since is part of the JDK thus bypassing the security checks.
Once again we can see the callers stack by debugging the applet:
Affected Versions
The com.sun.beans.finder.MethodFinder and
com.sun.beans.finder.ClassFinder classes are available only since JDK
7.
Putting all together
So this exploit is performing the following steps:
- Creates a Statement instance that will call
System.setSecurityManager(null) method using reflection.
- Creates a custom AccessControlContext with full permissions.
- With one bug it gets a reference to the sun.awt.SunToolkit
class that is restricted to
applets.
- With the other bug it
invokes the getField
public static method on sun.awt.SunToolkit
using reflection with a trusted immediate caller that bypasses the
security checks.
- With the getField
method it is getting a reference to Statement.acc
private field and setting its value to the custom
AccessControlContext instance previously created.
- Finally it executes the Statement
that will disable the Security Manager bypassing all security checks
because it has full permissions set in its AccessControlContext.
Author
Esteban Guillardoy
esteban@immunityinc.com
References
[1] - Secure Coding Guidelines for the Java
Programming Language, Version 4.0 -
http://www.oracle.com/technetwork/java/seccodeguide-139067.html
[2] - Java SE 7 Security Architecture -
http://docs.oracle.com/javase/7/docs/technotes/guides/security/spec/security-specTOC.fm.html
[3] - Java SE 7 Security Documents -
http://docs.oracle.com/javase/7/docs/technotes/guides/security/
[4] - Sami Koivu Blog - Java Trusted Method Chaining
(CVE-2010-0840/ZDI-10-056) -
http://slightlyrandombrokenthoughts.blogspot.com.ar/2010/04/java-trusted-method-chaining-cve-2010.html