Tuesday, August 14, 2007

Hack any Java class using Reflection

Ever wondered what evil power can be unleashed when using reflection? Do you think private methods are really only accessible from within the declaring class? Do you think that a private field can only be modified from within the declaring class? No? That's what I thought!! In this blog, I will try to demonstrate that it is always important to correctly set the security properties of your applications. For instance, let's look at the following example where we successfully retrieve a private password from another class:

Code:

class A {
private static String getPassword() {
return "someHighlyPreciousPassword";
}
}

public class Test {
public static void main(String[] args) throws Exception {
Class cl = Class.forName("A");
java.lang.reflect.Method[] m = cl.getDeclaredMethods();
m[0].setAccessible(true);
String password = (String) m[0].invoke(null, null);
System.out.println("I got it:" + password);
}
}

Output:

I got it: someHighlyPreciousPassword
Ok, the example is not really sexy. Let's mess up a class that implements the Singleton pattern. In the normal case, a singleton object is supposed to be the only instance of a given class. To achieve this, we usually declare the class constructor private, so that no one can invoke it. Well, as demonstrated below, with reflection we can bypass this restriction and create a second "singleton object".

Code:

class A {
public static final A singleton = new A("I'm the only instance of class A");
private String name;
private A(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
}

public class Test {
public static void main(String[] args) throws Exception {
Class cl = Class.forName("A");
java.lang.reflect.Constructor[] c = cl.getDeclaredConstructors();
c[0].setAccessible(true);
A anotherA = (A) c[0].newInstance(new Object[]{"Not anymore!!"});
System.out.println(A.singleton);
System.out.println(anotherA);
}
}

Output:

I'm the only instance of class A

 Not anymore!!
Using this technique, you can create an instance of any non-abstract class, even if all its constructors are declared private. For instance, below we create an instance of the Math class even though it is useless since the Math class has no instance method. Still, it is possible to do it.

Code:

public class Test {
public static void main(String[] args) throws Exception {
Class cl = Class.forName("java.lang.Math");
java.lang.reflect.Constructor[] c = cl.getDeclaredConstructors();
c[0].setAccessible(true);
Math mathInstance = (Math) c[0].newInstance(null);
System.out.println(mathInstance);
}
}

Output:
java.lang.Math@1cde100

Finally, let's mess with the Runtime class which has one private static field for storing the current Runtime instance. This is another example of a badly implemented singleton class. Let's look at the code below. We first retrieve the current runtime object and display it (3-4). Then, we set the Runtime.currentRuntime static field to null, which means that all successive calls to Runtime.getRuntime() will yield null (6-9) since currentRuntime is initialized at class loading time. We then get the currentRuntime field again and display its value (11-12). And finally, we try to use the current runtime to execute a command for displaying the content of the current directory (14). The output talks for itself.



Code:

public class Test {
public static void main(String[] args) throws Exception {
Runtime r = Runtime.getRuntime();
System.out.println("Before: Runtime.getRuntime() yields " + r);
Class cl = Class.forName("java.lang.Runtime");
java.lang.reflect.Field f = cl.getDeclaredField("currentRuntime");
f.setAccessible(true);
f.set(null, null);
r = Runtime.getRuntime();
System.out.println("After: Runtime.getRuntime() yields " + r);
r.exec("dir"); //raises NullPointerException!!
}
}

All this could have been avoided if the currentRuntime field had been declared final. Nothing prevents setAccessible(true) to be called on the field (8) but when the set(null, null) method is called, IllegalAccessException is thrown with the message "Field is final".

I'm pretty sure that there is a huge amount of code out there that could be broken this way. Watch out!!
Bottom line: singleton fields should always be declared private static final!!! Moreover, make sure you never grant ReflectPermission and RuntimePermission.accessDeclaredMembers in the java.policy file of your production code.



No comments: