Cover Image

n00bzCTF 2023 - EZrev


Énoncé

Dans ce challenge, on nous a donné une classe Java téléchargeable ici,

Pour reverse engineer une classe Java, nous aurons besoin d’un decompilateur Java, j’ai décidé d’utiliser jadx-gui.

Analyse du code initial

En analysant le code source, nous avons la fonction main() qui contient le code suivant:


package defpackage;

import java.util.Arrays;

/* renamed from: EZrev  reason: default package */
/* loaded from: EZrev.class */
public class EZrev {
    public static void main(String[] strArr) {
        if (strArr.length != 1) {
            System.out.println("L");
            return;
        }
        String str = strArr[0];
        if (str.length() != 31) {
            System.out.println("L");
            return;
        }
        int[] array = str.chars().toArray();
        for (int i = 0; i < array.length; i++) {
            if (i % 2 == 0) {
                array[i] = (char) (array[i] ^ 19);
            } else {
                array[i] = (char) (array[i] ^ 55);
            }
        }
        for (int i2 = 0; i2 < array.length / 2; i2++) {
            if (i2 % 2 == 0) {
                array[i2] = (char) (array[(array.length - 1) - i2] + 20);
                array[(array.length - 1) - i2] = (char) (array[i2] - 10);
            } else {
                array[i2] = (char) (array[i2] + 30);
            }
        }
        if (Arrays.equals(array, new int[]{130, 37, 70, 115, 64, 106, 143, 34, 54, 134, 96, 98, 125, 98, 138, 104, 25, 3, 66, 78, 24, 69, 91, 80, 87, 67, 95, 8, 25, 22, 115})) {
            System.out.println("W");
        } else {
            System.out.println("L");
        }
    }
}

Ce programme Java accepte une seule chaîne de caractères comme argument en ligne de commande et effectue une série de manipulations et de contrôles sur cette chaîne. Voici les étapes de son fonctionnement :

  1. Vérifie si un seul argument est passé au programme. Si le nombre d’arguments est différent de 1, il imprime "L" et termine l’exécution.

  2. Vérifie si la longueur de la chaîne fournie est exactement 31 caractères. Si ce n’est pas le cas, il imprime "L" et termine l’exécution.

  3. Convertit la chaîne de caractères en un tableau d’entiers, chaque entier représentant le code ASCII d’un caractère. Pour chaque caractère, si l’index du caractère est pair, il effectue une opération XOR (exclusive OR) avec 19, sinon, il effectue une opération XOR avec 55.

  4. Puis, il modifie encore le tableau. Pour chaque élément du tableau jusqu’à sa moitié, si l’index est pair, il échange les valeurs entre l’index courant et l’index symétrique par rapport au milieu du tableau, tout en ajustant les valeurs avec une addition ou une soustraction. Si l’index est impair, il ajoute simplement 30 à la valeur à l’index courant.

  5. Enfin, il compare le tableau résultant à un tableau prédéfini. Si les deux tableaux sont identiques, il imprime "W", sinon, il imprime "L".

Le but ici est de trouver la chaine de caractère qui permet de valider la condition et ainsi afficher "W".

Exploitation

Nous devons faire toutes les actions inverses pour retrouver la chaîne, et voici le code qui permet de le faire:

public class Test {
    public static void main(String[] args) {
        int[] targetArray = {130, 37, 70, 115, 64, 106, 143, 34, 54, 134, 96, 98, 125, 98, 138, 104, 25, 3, 66, 78, 24, 69, 91, 80, 87, 67, 95, 8, 25, 22, 115};
        char[] result = new char[31];

        // Reverse the second part of the transformations
        for (int i = 0; i < 31 / 2; i++) {
            if (i % 2 == 0) {
                int tmp = targetArray[i];
                targetArray[i] = (char) (targetArray[30 - i] + 10);
                targetArray[30 - i] = (char) (tmp - 20);
            } else {
                targetArray[i] -= 30;
            }
        }

        // Reverse the first part of the transformations
        for (int i = 0; i < 31; i++) {
            if (i % 2 == 0) {
                result[i] = (char) (targetArray[i] ^ 19);
            } else {
                result[i] = (char) (targetArray[i] ^ 55);
            }
        }

        // Print the reversed string
        System.out.println(new String(result));
    }
}

En executant le programme nous avons le résultat suivant:

$ javac Test.java; java Test
n00bz{r3v_1s_s0_e4zy_r1ght??!!}

Le flag est donc n00bz{r3v_1s_s0_e4zy_r1ght??!!}