Issue 54 in Java
Introduction
One of the quite recent (at least, not too old) and amusing things to look at when you are beginning to study security in java is the issue 54 from Security Exploitation. This issue is quite interesting, because it is a low level trick and is, so far, not patched.
Security in java
Before talking about this particular issue, let’s see some basics about security in Java in general.
The first thing to know is what and why are we attacking java? Java is designed to run code from untrusted sources securely. This is a well known property and you can find it “everyday” in your browser with the java applets. When an applet is downloaded from a website the browser will run it and you don’t want a potentially malicious attacker to have full permissions under your machine.
Java implements a system of permissions to limit possibilities for the code executed with unprivileged rights (the applet). The goal for an attacker will be to acquire full privileges from an unprivileged application, allowing full jeopardy of the computer. The traditionnal attacks (overflow, use-after-free…) are still working but there is an additional type which is less common : the sandbox bypass (which can itself be divided in several parts: unsafe reflection, least privilege violation…).
The security of Java is based on several things, the first is the gestion of the memory which is handled by the JVM (Java Virtual Machine) and not by the user. It first avoids most of the stupid errors developers can make, but it is also mandatory for running code safely (if we can do what we want with the memory we already have the same privilege that the program).
The second part of the security is handled at the loading of a class. This loading process is divided into two parts: the class loader and the bytecode verifier.
The class loader has a similar goal as the dynamic linker in Unix systems.
There are several implementations (classes) of the class loader, like the applet
class loader which can load code over the internet from a website. All
class loaders inherit from java.lang.ClassLoader
. Of course, a class loader has
to take some precautions not to execute malicious code. In particular, it will
have to check that we are not trying to spoof a System class which would allow
us to bypass all security protections.
During the validation step by the class loader, the bytecode will be checked by
the bytecode verifier. It is called from the class loader through the method
defineClass
. It will not perform any check of logic but only check that the
bytecode is valid and other various things, for example that it is not
overflowing the stack. Once the bytecode verifier has done his work, if the
class loader validates the class, the code is considered to be of no harm to
the JVM (this doesn’t mean you have all the privileges).
The last important part in java security is the security manager, it’s the part
which will check all the permissions during runtime. If unprivileged code tries
to do something forbidden, it will raise an exception. The basic class for the
security manager is java.lang.SecurityManager
Usually, the security manager
will be retrieved by a call to getSecurityManager
(java.lang.System
).
If the security manager is set to null
, no check is performed and the code
runs with full privileges. Therefore, the goal of a lot of exploits will be to
rewrite the security manager to null
. Some permissions allow to change the
security manager and to set it to null
(AllPermission
, setSecurityManager
,
createClassLoader
, accessClassInPackage.sun
…). Typically, a permission
check looks like this:
::java
// From AppletClassLoader.java
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPackageAccess(name.substring(0, i));
The method checkPackageAccess
and all the other check functions will throw an
error if the code doesn’t have the rights to perform the action desired.
The check looks into the stack-call and if it finds an unprivileged function,
it throws an exception. To go from unprivileged code to privileged code,
Java uses the ActionController.doPrivileged
method:
::java
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
// insert priviledge code here
}
});
This check is performed by the security manager and will stop at the first doPrivileged it finds.
The MethodHandle resolution mecanism
In the constant pool of a class file it is possible to define a MethodHandle
.
This entry in the constant pool contains two elements: the reference kind and
the reference index. The reference kind characterizes the bytecode behavior of
the methodhandle, there are 9 possible kinds, as follow:
REF_getField
REF_getStatic
REF_putField
REF_putStatic
REF_invokeVirtual
REF_invokeStatic
REF_invokeSpecial
REF_newInvokeSpecial
REF_invokeInterface
All 9 kinds are used to get a MethodHandle
. This object can reference not
only methods but also fields, constructors “and similar low-level operations”.
The kinds 1 to 4 are used to create a MethodHandle
on a field and the
reference index must point to a CONSTANT_Fieldref
. For kinds 5 to 8 the
reference index must point to a CONSTANT_Methodref
. It is used to get a
MethodHandle
on a method.
The last kind (REF_invokeInterface
) is used for CONSTANT_InterfaceMethodref
and returns a MethodHandle
for an interface method. The interesting part
about the use of a CONSTANT_MethodHandle
into the constant pool is that the
creation of the MethodHandle
is done at the loading of the class file.
Theoretically, it should make no difference between retrieving the
MethodHandle
at the loading of the class and after the loading. We will
see that it’s not the case.
Issue 54: the vulnerability
The issue 54 has been found by Security Exploitation and is well documented
(http://www.security-explorations.com/materials/se-2012-01-54.pdf).
The usual way to get a Method Handler of a function in a class is to call the
public method findVirtual
from the MethodHandles.Lookup
module.
The code of this method is the following:
::java
public MethodHandle findVirtual(Class<?> refc, String name, MethodType type)
throws NoSuchMethodException, IllegalAccessException {
MemberName method = resolveOrFail(refc, name, type, false);
checkSecurityManager(refc, method);
return accessVirtual(refc, method);
}
In this code we can see the call to the method checkSecurityManager
which checks
whether the calling code has the right to get the MethodHandle
. In particular,
it will forbid to get a MethodHandle
on a private method of a super-class.
On the other hand, when getting a MethodHandle at class loading with a
REF_invokeVirtual
, the method called is resolveVirtual
:
::java
private MethodHandle resolveVirtual(Class<?> refc, String name, MethodType
type) throws NoSuchMethodException, IllegalAccessException {
MemberName method = resolveOrFail(refc, name, type, false);
return accessVirtual(refc, method);
}
We can see that the only difference between these two functions is the call to
the checkSecurityManager
function which is not done in the resolveVirtual
method. The resolveVirtual
function is of course private but during loading it
is called by the class loader. That means that a specially crafted class can
get virtual and static methods (the same issue exists with findStatic and
resolveStatic) from a class, allowing to have a valid MethodHandle
on
something we shouldn’t have had access to.
This issue is also present in most of the different kinds of
CONSTANT_MethodHandle
entries in the constant pool of a class file.
Still, this issue alone does not allow to execute code from an untrusted source
as privileged. When Security Exploitation reported that vulnerability,
they used a second one (Issue 55
http://www.security-explorations.com/materials/SE-2012-01-ORACLE-10.pdf) to
get the execution of code as privileged. The issue 55 allows to bind a
MethodHandle
to an object instance of incompatible type. This could allow to set
the securitymanager to null, bypassing all the permission protection.
Issue 54: the exploitation
With this issue, Security Exploitation has released a demonstration of the
the issues 54 and 55
(http://www.security-explorations.com/materials/se-2012-01-50-60.zip). In
particular, it contains a class MyCL.class
which is “hand made”, and contains
the exploitation of issue 54.
Here is the constant pool of this class:
::java
CONSTANT_MethodRef(10) 5, 16
CONSTANT_MethodRef(10) 5, 17
CONSTANT_String(8) 10
CONSTANT_Class(7) 18
CONSTANT_Class(7) 19
CONSTANT_Utf8(1) 6 : <init>
CONSTANT_Utf8(1) 3 : ()V
CONSTANT_Utf8(1) 4 : Code
CONSTANT_Utf8(1) 15 : LineNumberTable
CONSTANT_Utf8(1) 5 : dummy
CONSTANT_Utf8(1) 57 : (Ljava/lang/String;[BIILjava/security/ProtectionDomain;)V
CONSTANT_Utf8(1) 18 : get_defineClass_mh
CONSTANT_Utf8(1) 20 : ()Ljava/lang/Object;
CONSTANT_Utf8(1) 10 : SourceFile
CONSTANT_Utf8(1) 9 : MyCL.java
CONSTANT_NameAndType(12) 6, 7
CONSTANT_NameAndType(12) 20, 21
CONSTANT_Utf8(1) 4 : MyCL
CONSTANT_Utf8(1) 21 : java/lang/ClassLoader
CONSTANT_Utf8(1) 11 : defineClass
CONSTANT_Utf8(1) 73 : (Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;
CONSTANT_MethodHandle(15) REF_invokeVirtual(5), 2
We can see that the entry 22 is a CONSTANT_MethodHandle1
with the kind
REF_invokeVirtual
for exploiting the vulnerability and refer to the
CONSTANT_MethodRef
at the entry 2, which is in the class java.lang.ClassLoader
the method defineClass
.
The defineClass
method in java.lang.ClassLoader
is a protected final method
and it should be impossible to have an handle on this method and obviously to
call it.
In MyCL.class
we have three methods :
<init>
which is for the initialisationdummy
get_defineClass_mh
which returns theCONSTANT_MethodHandle
at the entry 22 of the constant pool.
The other part of the exploit is for the issue 55 which will allow to bind the method handle to another class and get it called with privileged rights, allowing a sandbox bypass.
Conclusion
This issue, even if a little old, is really interesting because it puts in light some internals of the class loading process which are often unclear. It is also really disturbing because Oracle doesn’t seem to consider this issue as a problem, indicating that this was an “allowed behavior”. Still not patched, this issue can be used for developing exploits, enlarging the possibility for finding vulnerabilities. Even if this issue was basically focused on a MethodHandle pointing to a method, the same problem exists with MethodHandle pointing on a field, allowing to gain even more access.