Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Java is it possible to change or modify an enum itself and thus to corrupt an enum singleton?

Is it possible to change an enum itself at run-time somehow? E.g. using reflection. The question is not about to change the state of an enum constant. It's about to change the set of constants of an enum or to remove any constants.

Regarding the following enum is it possible to add a color WHITE or remove color RED or to change their order?

public enum Color {

  RED, GREEN, BLUE;

}

Why I ask?

  • First of all I want to know if it is feasible.
  • But if it is, would it have an impact on the common way to implement a singleton using an Enum?

I know this question is kind of malicious. But even Joshua Bloch mentioned a "cleverly crafted attack" when talking (1) about implementing a singleton and recommended the enum singleton pattern. If we can modify an enum is then an attack on this pattern feasible?

I tried to work it out and managed it partially. I will post my results as an answer - following this advice.


(1) See What is an efficient way to implement a singleton pattern in Java? There follow the link to effective_java_reloaded.pdf, page 31.

like image 314
LuCio Avatar asked Jul 07 '18 13:07

LuCio


2 Answers

I started my analysis disassembling the enum Color using javap -c. Here is an excerpt from:

 static {};
    Code:
       0: new           #1                  // class playground/Color
       3: dup           
       4: ldc           #14                 // String RED
       6: iconst_0      
       7: invokespecial #15                 // Method "<init>":(Ljava/lang/String;I)V
      10: putstatic     #19                 // Field RED:Lplayground/Color;
      13: new           #1                  // class playground/Color
      16: dup           
      17: ldc           #21                 // String GREEN
      19: iconst_1      
      20: invokespecial #15                 // Method "<init>":(Ljava/lang/String;I)V
      23: putstatic     #22                 // Field GREEN:Lplayground/Color;
      26: new           #1                  // class playground/Color
      29: dup           
      30: ldc           #24                 // String BLUE
      32: iconst_2      
      33: invokespecial #15                 // Method "<init>":(Ljava/lang/String;I)V
      36: putstatic     #25                 // Field BLUE:Lplayground/Color;
      39: iconst_3      
      40: anewarray     #1                  // class playground/Color
      43: dup           
      44: iconst_0      
      45: getstatic     #19                 // Field RED:Lplayground/Color;
      48: aastore       
      49: dup           
      50: iconst_1      
      51: getstatic     #22                 // Field GREEN:Lplayground/Color;
      54: aastore       
      55: dup           
      56: iconst_2      
      57: getstatic     #25                 // Field BLUE:Lplayground/Color;
      60: aastore       
      61: putstatic     #27                 // Field ENUM$VALUES:[Lplayground/Color;
      64: return        

At index 61 we see the three enum constants being assigned to a static array with name ENUM$VALUES.

Listing all static fields by reflection ...

Field[] declaredFields = Color.class.getDeclaredFields();
for (Field field : declaredFields) {
  if (Modifier.isStatic(field.getModifiers())) {
    System.out.println(field.getName() + ": " + field.getType());
  }
}

shows the enum constants and reveals the array:

RED: class playground.ReflectEnum$Color
GREEN: class playground.ReflectEnum$Color
BLUE: class playground.ReflectEnum$Color
ENUM$VALUES: class [Lplayground.ReflectEnum$Color;

I defined the following method to get the enum array:

  protected static <E extends Enum<E>> E[] getEnumsArray(Class<E> ec) throws Exception {
    Field field = ec.getDeclaredField("ENUM$VALUES");
    field.setAccessible(true);
    return (E[]) field.get(ec);
  }

Using it it's possible to change the order of the enum constants:

Color[] colors = getEnumsArray(Color.class);
colors[0] = Color.GREEN;
colors[1] = Color.RED;
colors[2] = Color.BLUE;

Listing the enum constants

for (Color color : Color.values()) {
  System.out.println(action + ":" + color.ordinal());
}

shows:

GREEN:1
RED:0
BLUE:2

Obviously the order has been changed.

Since it is possible to assign values to the array it is also valid to assign null.

Color[] colors = getEnumsArray(Color.class);
colors[0] = Color.GREEN;
colors[1] = Color.RED;
colors[2] = null;

Listing the enum constants shows:

GREEN:1
RED:0
Exception in thread "main" java.lang.NullPointerException
    at playground.ReflectEnum.main(ReflectEnum.java:57)

And if we try look up the enum constants by their names

System.out.println(Color.valueOf("GREEN"));
System.out.println(Color.valueOf("RED"));
System.out.println(Color.valueOf("BLUE"));

we see that the last change broke even more:

Exception in thread "main" java.lang.NullPointerException
    at java.lang.Class.enumConstantDirectory(Class.java:3236)
    at java.lang.Enum.valueOf(Enum.java:232)
    at playground.Color.valueOf(Color.java:1)
    at playground.ReflectEnum.main(ReflectEnum.java:48)

The line ReflectEnum.java:48 contains the above print statement of Color.valueOf("GREEN"). This enum constant was not set to null. So it broke the valueOf method of Color completely.

But Enum.valueOf(Color.class, "BLUE") still resolves the enum constant Color.BLUE.

Since the enum array is declared as static final I followed Change private static final field using Java reflection to create and set a new enum array.

  protected static <E extends Enum<E>> void setEnumsArray(Class<E> ec, E... e) throws Exception {
    Field field = ec.getDeclaredField("ENUM$VALUES");
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    field.setAccessible(true);
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(ec, e);
  }

But executing setEnumsArray(Color.class, Color.BLUE, Color.GREEN, Color.RED, Color.BLUE) fails with

Exception in thread "main" java.lang.IllegalAccessException: Can not set static final [Lplayground.Color; field playground.Color.ENUM$VALUES to [Lplayground.Color;
    at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
    at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
    at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
    at java.lang.reflect.Field.set(Field.java:758)
    at playground.ReflectEnum.setEnumsArray(ReflectEnum.java:76)
    at playground.ReflectEnum.main(ReflectEnum.java:37)

EDIT 1 (after comments of Radiodef):

After I added the following two methods ...

  protected static Field getEnumsArrayField(Class<?> ec) throws Exception {
    Field field = ec.getDeclaredField("ENUM$VALUES");
    field.setAccessible(true);
    return field;
  }

  protected static void clearFieldAccessors(Field field) throws ReflectiveOperationException {
    Field fa = Field.class.getDeclaredField("fieldAccessor");
    fa.setAccessible(true);
    fa.set(field, null);

    Field ofa = Field.class.getDeclaredField("overrideFieldAccessor");
    ofa.setAccessible(true);
    ofa.set(field, null);

    Field rf = Field.class.getDeclaredField("root");
    rf.setAccessible(true);
    Field root = (Field) rf.get(field);
    if (root != null) {
      clearFieldAccessors(root);
    }

i tried this:

System.out.println(Arrays.toString((Object[]) getEnumsArrayField(Color.class).get(null)));
clearFieldAccessors(getEnumsArrayField(Color.class));
setEnumsArray(Color.class, Color.BLUE, Color.GREEN, Color.RED, Color.BLUE);
System.out.println(Arrays.toString(Color.values()));

This shows:

[RED, GREEN, BLUE]
[BLUE, GREEN, RED, BLUE]

The enum array has been replaced by another one.


EDIT 2 (after comments of GotoFinal)
According to the answer of GotoFinal on How to create an instance of enum using reflection in java? it's possible to create further enum instances at run-time. Then it should be possible to replace an enum singleton instance by another one. I have the following enum singleton:

  public enum Singleton {

    INSTANCE("The one and only");

    private String description;

    private Singleton(String description) {
      this.description = description;
    }

    @Override
    public String toString() {
      return description;
    }

  }

Reusing the code GotoFinal has shown I defined the following method:

  protected static Singleton createEnumValue(String name, int ordinal, String description) throws Exception {
    Class<Singleton> monsterClass = Singleton.class;
    Constructor<?> constructor = monsterClass.getDeclaredConstructors()[0];
    constructor.setAccessible(true);

    Field constructorAccessorField = Constructor.class.getDeclaredField("constructorAccessor");
    constructorAccessorField.setAccessible(true);
    sun.reflect.ConstructorAccessor ca = (sun.reflect.ConstructorAccessor) constructorAccessorField.get(constructor);
    if (ca == null) {
      Method acquireConstructorAccessorMethod = Constructor.class.getDeclaredMethod("acquireConstructorAccessor");
      acquireConstructorAccessorMethod.setAccessible(true);
      ca = (sun.reflect.ConstructorAccessor) acquireConstructorAccessorMethod.invoke(constructor);
    }
    Singleton enumValue = (Singleton) ca.newInstance(new Object[] { name, ordinal, description });
    return enumValue;
  }

 protected static <E extends Enum<E>> void setFinalField(Class<E> ec, Field field, E e) throws NoSuchFieldException, IllegalAccessException {
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    field.setAccessible(true);
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(ec, e);
  }

Now executing

System.out.println(Singleton.INSTANCE.toString());
// setting INSTANCE = theNewOne
Singleton theNewOne = createEnumValue(Singleton.INSTANCE.name(), Singleton.INSTANCE.ordinal(), "The new one!");
setFinalField(Singleton.class, Singleton.class.getDeclaredField(Singleton.INSTANCE.name()), theNewOne);
System.out.println(Singleton.INSTANCE.toString());
// setting enum array = [theNewOne]
clearFieldAccessors(getEnumsArrayField(Singleton.class));
setEnumsArray(Singleton.class, theNewOne);
System.out.println(Arrays.toString(Singleton.values()));

shows:

The one and only
The new one!
[The new one!]

Summarizing:

  • It's possible to modify an enum at run-time and to replace the enum array by another one. But at least setting an enum constant to null breaks the consistency of defined enums within the VM. Though this can be fixed according to GotoFinal's answer.

  • When implementing a singleton using enum it's possible to replace the only one instance by another enum instance - at least for some JDKs/JREs knowing their implementations. If the enum constructor has arguments they can be exploited by the new created enum instance to plant malicious behaviour.

like image 186
LuCio Avatar answered Oct 04 '22 04:10

LuCio


Yes, you can even add new values to enum without any issues, as I already explained here: https://stackoverflow.com/a/51244909/4378853

So you have multiple ways to break enum:
1. Use reflections to change value of fields like Color.RED to some other enum value.
2. Use reflections to change .values by modifying ENUM$VALUES field.
3. Use reflections to change what MyEnum.class.getEnumConstants() returns by editing T[] enumConstants in Class class.
4. Similar for Map<String, T> enumConstantDirectory in Class class - used for Enum.valueOf(MyEnum.class, name) and MyEnum.valueOf(name) .
5. Use reflections to add new enum constant as described in my linked answer above.

So for

Regarding the following enum is it possible to add a color WHITE or remove color RED or to change their order?

Yes, it is possible.

Class<Color> enumClass = Color.class;
// first we need to find our constructor, and make it accessible
Constructor<?> constructor = enumClass.getDeclaredConstructors()[0];
constructor.setAccessible(true);

// this is this same code as in constructor.newInstance, but we just skipped all that useless enum checks ;)
Field constructorAccessorField = Constructor.class.getDeclaredField("constructorAccessor");
constructorAccessorField.setAccessible(true);
// sun.reflect.ConstructorAccessor -> iternal class, we should not use it, if you need use it, it would be better to actually not import it, but use it only via reflections. (as package may change, and will in java 9)
ConstructorAccessor ca = (ConstructorAccessor) constructorAccessorField.get(constructor);
if (ca == null) {
    Method acquireConstructorAccessorMethod = Constructor.class.getDeclaredMethod("acquireConstructorAccessor");
    acquireConstructorAccessorMethod.setAccessible(true);
    ca = (ConstructorAccessor) acquireConstructorAccessorMethod.invoke(constructor);
}
// note that real constructor contains 2 additional parameters, name and ordinal
Color enumValue = (Color) ca.newInstance(new Object[]{"WHITE", 3});// you can call that using reflections too, reflecting reflections are best part of java ;)


static void makeAccessible(Field field) throws Exception {
    field.setAccessible(true);
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~ Modifier.FINAL);
}

And then just change that field to new value that include our new field:

Field valuesField = Color.class.getDeclaredField("$VALUES");
makeAccessible(valuesField);
// just copy old values to new array and add our new field.
Color[] oldValues = (Color[]) valuesField.get(null);
Color[] newValues = new Color[oldValues.length + 1];
System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
newValues[oldValues.length] = enumValue;
valuesField.set(null, newValues);

And now also use reflections to update that two fields inside Class class.

Field enumConstantDirectoryField = Class.class.getDeclaredField("enumConstantDirectory");
enumConstantDirectoryField.setAccessible(true);
enumConstantDirectoryField.set(Color.class, null);
Field enumConstantsField = Class.class.getDeclaredField("enumConstants");
enumConstantsField.setAccessible(true);
enumConstantsField.set(Color.class, null);

To change order just use this same code as for $VALUES field edit, but just set it to whatever you want.

You can't only add/remove fields like make Color.WHITE work or make Color.RED disappear (but you can set it to null)

Also using unsafe it should be possible to declare new class to create new instance of abstract enum classes.
I used javassist library to reduce code needed to generate new class:

public class Test {
    public static void main(String[] args) throws Exception {
        System.out.println(MyEnum.VALUE.getSomething()); // prints 5

        ClassPool classPool = ClassPool.getDefault();
        CtClass enumCtClass = classPool.getCtClass(MyEnum.class.getName());
        CtClass ctClass = classPool.makeClass("com.example.demo.MyEnum$2", enumCtClass);

        CtMethod getSomethingCtMethod = new CtMethod(CtClass.intType, "getSomething", new CtClass[0], ctClass);
        getSomethingCtMethod.setBody("{return 3;}");
        ctClass.addMethod(getSomethingCtMethod);

        Constructor<?> unsafeConstructor = Unsafe.class.getDeclaredConstructors()[0];
        unsafeConstructor.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeConstructor.newInstance();

        MyEnum newInstance = (MyEnum) unsafe.allocateInstance(ctClass.toClass());
        Field singletonInstance = MyEnum.class.getDeclaredField("VALUE");
        makeAccessible(singletonInstance);
        singletonInstance.set(null, newInstance);

        System.out.println(MyEnum.VALUE.getSomething()); // prints 3
    }

    static void makeAccessible(Field field) throws Exception {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~ Modifier.FINAL);
    }
}

enum MyEnum {
    VALUE {
        @Override
        public int getSomething() {
            return 5;
        }
    };

    public abstract int getSomething();
}

This will print 5 and then 3. Note that this is impossible to enum classes that does not contain subclasses - so without any overriden methods, as then enum is declared as final class.

And you can use that to break singleton pattern without any issues, just set it to null, or you can even try to use unsafe to implement own version of it at runtime and replace instance of it, or just create multiple instances of it.
This is why I prefer just simple class with private constructor and holder class - if someone want to break it, they will do it anyway. But at least you don't use enum to something it is not designed for, so code is easier to read.

like image 43
GotoFinal Avatar answered Oct 04 '22 03:10

GotoFinal