Hack any Java class using reflection attack

Have you ever thought how secure your application is? Well reflection attack can demonstrate how vulnerable Java classes are. In this post, I will try to apply reflection attack on a simple Java class to demonstrate vulnerabilities and what can be done to prevent it (in most cases).

Consider a POJO class:

[java]
public final class VictimClass {

private String password = "default_password";
private static final int USER_ID = 3452678;
private String privateFiled = "default_value";

/**
* Private constructor that should
* not be invoked
*/
private VictimClass() {
System.out.println("Oops… " +
"This private constructor was not " +
" suppose to be invoked");
}

/**
* Private accessor
*/
private String getPassword() {
return password;
}

/**
* Private mutator
*/
private void setPassword(String password) {
this.password = password;
}

/**
* Private static method
*/
private static int getUsersId() {
return USER_ID;
}
}

[/java]

The POJO contains private constructor and several private methods and fields, which I will attempt to invoke and modify using reflection attack.

Now, I cannot say that reflection attacks are possible due to a Java bug. No, its simply how Java classes were designed. The core reflection facility was originally designed for component based application builder tools.

In java.lang.reflect, Constructor, Method and Field extend from parent AccessibleObject class. These objects provide access to the class’s methods and fields. By calling inherited parent method setAccessible(), private variables and methods including private constructors become accessible.

My tester class:

[java]
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.ClassNotFoundException;
import java.lang.InstantiationException;
import java.lang.IllegalAccessException;
import java.lang.reflect.InvocationTargetException;
import java.lang.NoSuchMethodException;

public class Tester {

private static String CLASS_NAME = "VictimClass";
private static Class victimClass = null;
private static Object victimClassObj = null;

public static void main(String[] args) {
victimClass = loadClass(victimClass, CLASS_NAME);
printClassStructure();
attack();
}

private static Class loadClass(Class clazzor, String className) {
Thread thread = Thread.currentThread();
ClassLoader classLoader =
thread.getContextClassLoader();

try {
clazzor = Class.forName(className, true, classLoader);
}
catch (ClassNotFoundException e) {
System.err.println("Error: could not find class: "
+ CLASS_NAME);
}

return clazzor;
}

private static void printClassStructure() {

Constructor[] constructors =
victimClass.getDeclaredConstructors();
for (Constructor c : constructors) {
int modifier = c.getModifiers();
System.out.println("Declared constructor name: " +
c.getName() + "ntis accessible: " +
c.isAccessible() + "ntis private: " +
Modifier.isPrivate(modifier) + "n");
}

Method[] methods = victimClass.getDeclaredMethods();
for (Method m : methods) {
int modifier = m.getModifiers();
System.out.println("Declared method name: " + m.getName() +
"ntis accessible: " +
m.isAccessible() +
"ntis private: " +
Modifier.isPrivate(modifier) +
"ntis static: " +
Modifier.isStatic(modifier) + "n");
}

Field[] fields = victimClass.getDeclaredFields();
for (Field f : fields) {
int modifier = f.getModifiers();
System.out.println("Declared field name: " + f.getName() +
"ntis accessible: " +
f.isAccessible() +
"ntis private: " +
Modifier.isPrivate(modifier) +
"ntis static: " +
Modifier.isStatic(modifier) +
"ntis final: " +
Modifier.isFinal(modifier) + "n");
}
}

private static void attack() {

Field[] fields = victimClass.getDeclaredFields();
Method[] methods = victimClass.getDeclaredMethods();
Constructor[] constructors =
victimClass.getDeclaredConstructors();
//make constructor accessible
constructors[0].setAccessible(true);

System.err.println("Initiating reflection attack:");
try {
//create new object by invoking private constructor
victimClassObj = constructors[0].newInstance(new Object[] {});

//make static method accessible and get its value
//please note: when invoking static method,
//object represented by this Method is null
methods[2].setAccessible(true);
Object o = methods[2].invoke(null, new Object[] {});
System.out.println("Got user ID from private static accessor: "
+ o.toString());

//make method accessible and get its value
methods[0].setAccessible(true);
o = methods[0].invoke(victimClassObj, new Object[] {});
System.out.println("Got original password from private accessor: "
+ o.toString());

//make method accessible and set to it new value
methods[1].setAccessible(true);
System.out.println("Injecting new password using private mutator");
methods[1].invoke(victimClassObj, new Object[] {"injected_password"});

//get method’s its new value
o = methods[0].invoke(victimClassObj, new Object[] {});
System.out.println("Got injected password from private accessor: "
+ o.toString());

//make field accessible and get its value
fields[2].setAccessible(true);
o = fields[2].get(victimClassObj);
System.out.println("Got private field: " + o);

//make field accessible and set to it new value
System.out.println("Injecting value to a private field:");
fields[2].set(victimClassObj, "new_default_value");

//get field’s its new value
o = fields[2].get(victimClassObj);
System.out.println("Got updated private field: " + o);

//make field accessible and get its value
fields[1].setAccessible(true);
o = fields[1].get(victimClassObj);
System.out.println("Got private static field: " + o);

//make field accessible and set to it new value
System.out.println("Injecting value to a private static final field:");
fields[1].set(null, new Integer(2));

//get field’s its new value
o = fields[1].get(victimClassObj);
System.out.println("Got updated private static final field: " + o);

}
catch (InstantiationException e) {
System.err.println("Error: could not instantiate: " + e);
}

catch (IllegalAccessException e) {
System.err.println("Error: could not access: " + e);
}

catch (InvocationTargetException e) {
System.err.println("Error: could not invoke the target: " + e);
}
}
}

[/java]

The call to setAccessible() can be restricted if SecurityManager is set, then any attempt to call the method above will result in exception. If required, it is possible to grant permission java.reflect.ReflectPermission “suppressAccessChecks” using external java.policy file or by applying security policy programmatically. This permission will allow invocation of setAccessible(). My other post How to set SecurityManager and Java security policy programmatically explains how this can be achieved. Please note: by doing so, you can allow malicious access to your classes.

This is what happens when program executes:
I am using reflection, to get a print out of declared constructors, methods and fields. Following that, I am invoking private static and non-static method and attempting to change the value of private declared fields.

The program produces the following output:

[java]
Declared constructor name: VictimClass
is accessible: false
is private: true

Declared method name: setPassword
is accessible: false
is private: true
is static: false

Declared method name: getUsersId
is accessible: false
is private: true
is static: true

Declared method name: getPassword
is accessible: false
is private: true
is static: false

Declared field name: password
is accessible: false
is private: true
is static: false
is final: false

Declared field name: USER_ID
is accessible: false
is private: true
is static: true
is final: true

Declared field name: privateFiled
is accessible: false
is private: true
is static: false
is final: false

Initiating reflection attack:
Oops… This private constructor was not suppose to be invoked
Got user ID from private static accessor: 3452678
Got original password from private accessor: default_password
Injecting new password using private mutator
Got injected password from private accessor: injected_password
Got private field: default_value
Injecting value to a private field:
Got updated private field: new_default_value
Got private static field: 3452678
Injecting value to a private static final field:
Error: could not access: java.lang.IllegalAccessException: Field is final
[/java]

As output shows above, it is not possible to set a value to the filed that is final. Therefore, it would be good idea to make fields final whenever possible. As it was shown before, it prevents reflection attack (when trying to set a new value), plus to that its good for performance optimization (memory allocation).

So what we got so far? When we have a POJO, it is possible to invoke private constructor, private static and non-static methods and change the value of private fields.

Enumerators on other hand do not allow invocation of their private constructors. An attempt to do so will result in IllegalArgumentException exception: “Cannot reflectively create enum objects”. Enums are safe against invocation of private constructors through reflection attacks.

Since enum object instances cannot be created through reflection, private non-static methods cannot be invoked. Having said that, it is still possible to invoke private static methods on enum using reflection, like on any other Java class.

Update: private non-static methods can be invoked on enum class, please refer to comments.

In conclusion, number of steps can be taken, in case someone really wants to prevent reflection attack on non-enum Java classes:

  1. Make fields final whenever possible
  2. Private constructor can throw an exception if there will be an attempt to invoke it.
  3. Applying security policy without ReflectPermission and setting security manager

I hope that I was descriptive enough here, I will appreciate your comments here.

The source code for this post was tested in my Eclipse and is attached.

Cheers