Cover Image

n00bzCTF 2023 - MyPin


Énoncé

Dans ce challenge, on nous a donné un fichier jar, téléchargeable ici.

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

Analyse du code initial

En analysant le code source, nous avons la classe MyPin() qui contient le code suivant:


package defpackage;

import java.awt.LayoutManager;
import javax.swing.JFrame;
import javax.swing.JLabel;

/* renamed from: Mypin  reason: default package */
/* loaded from: My-pin.jar:Mypin.class */
public class Mypin {
    private JFrame frame = new JFrame("Guess the pin");
    private JLabel output = new JLabel();

    Mypin() {
        this.output.setBounds(20, 250, 360, 20);
        JLabel jLabel = new JLabel("Your data:");
        jLabel.setBounds(20, 235, 100, 20);
        this.frame.add(jLabel);
        this.frame.add(this.output);
        this.frame.add(new PinButton(this, "0", 43, 10, 150, 150));
        this.frame.add(new PinButton(this, "1", 198, 10, 150, 150));
        this.frame.add(new ResetButton(this, 43, 165, 305, 50));
        this.frame.setSize(400, 350);
        this.frame.setLayout((LayoutManager) null);
        this.frame.setVisible(true);
        this.frame.setDefaultCloseOperation(3);
    }

    public static void main(String[] strArr) {
        new Mypin();
    }

    public void clearOutput() {
        this.output.setText("");
    }

    public void updateOutput() {
        this.output.setText(Secret.getInstance().getData());
    }
}

Il s’agit d’un programme Java qui crée une interface graphique Swing pour un jeu intitulé "Guess the pin". Il semble que le but de ce jeu est d’entrer ou de deviner un code PIN. Le code utilise la classe JFrame pour créer une fenêtre et des JLabel pour afficher des informations à l’utilisateur.

Il y a des boutons pour les chiffres 0 et 1, qui sont probablement utilisés pour entrer le code PIN. Il y a aussi un bouton de réinitialisation qui, je suppose, réinitialise le code PIN entré.

La classe Secret est très suspicieuse, c’est sans doute la partie la plus intéressante à analyser pour découvrir le flag.

Voici ci dessous la classe Secret:

package defpackage;

/* JADX INFO: Access modifiers changed from: package-private */
/* compiled from: Mypin.java */
/* renamed from: Secret  reason: default package */
/* loaded from: My-pin.jar:Secret.class */
public class Secret {
    private static Secret instance = new Secret();
    private int cnt = 1;
    private int[] mydata = {0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0};
    private int[] box = new int[this.mydata.length / 9];

    private Secret() {
    }

    public static Secret getInstance() {
        return instance;
    }

    public void resetInstance() {
        instance = new Secret();
    }

    public void process(char c) {
        if (this.cnt > 9) {
            return;
        }
        int length = this.mydata.length / 9;
        for (int i = 1; i <= length; i++) {
            int i2 = (9 * i) - this.cnt;
            int i3 = this.box[i - 1] + this.mydata[i2] + (c - '0');
            this.mydata[i2] = i3 % 2;
            if (i3 >= 2) {
                this.box[i - 1] = 1;
            } else {
                this.box[i - 1] = 0;
            }
        }
        this.cnt++;
    }

    private String misteri(int i) {
        String str = "";
        int i2 = 0;
        int i3 = 1;
        while (i > 0) {
            i2 |= (i & 1) << ((i3 % 8) - 1);
            i >>= 1;
            if (i3 % 8 == 0) {
                if (32 <= i2 && i2 < 128) {
                    str = ((char) i2) + str;
                }
                i2 = 0;
            }
            i3++;
        }
        return ((char) i2) + str;
    }

    public String getData() {
        int length = this.mydata.length / 9;
        String str = "";
        int i = 5;
        int i2 = 0;
        for (int i3 = 1; i3 <= length; i3++) {
            int i4 = 0;
            int i5 = 1;
            for (int i6 = 1; i6 <= 8; i6++) {
                i4 += this.mydata[(9 * i3) - i6] * i5;
                i5 <<= 1;
            }
            i--;
            i2 = (int) (i2 + ((i4 - 33) * Math.pow(85.0d, i)));
            if (i == 0) {
                str = str + misteri(i2);
                i2 = 0;
                i = 5;
            }
        }
        while (i > 0) {
            i--;
            i2 = (int) (i2 + (84.0d * Math.pow(85.0d, i)));
        }
        return str + misteri(i2);
    }
}

Dans ce programme, chaque fois qu’un utilisateur entre un chiffre PIN à l’aide de l’interface graphique (via les boutons), la méthode process(char c) de la classe Secret est appelée avec le chiffre entré en paramètre. Cette méthode modifie alors les données internes (mydata et box) de la classe Secret en fonction de ce chiffre.

La méthode getData() est ensuite utilisée pour convertir ces données internes en une chaîne de caractères, qui est affichée dans l’interface utilisateur. Ainsi, en entrant une série de chiffres en tant que code PIN, le programme génère une sortie spécifique basée sur ces entrées.

Exploitation

Sachant qu’il faut découvrir un PIN, la methode judicieuse et rapide ici est de créer un programme Java qui va utiliser la classe Secret puis tester toutes les combinaisons de PIN. On peut remarquer qu’il y a seulement 512 PIN possibles, donc ça sera rapide à trouver.

Voici les codes que j’ai utilisés,

Dans un fichier Secret.java on ajoute la classe Secret, puis ensuite on créé un fichier Pin.java:

public class Pin {
    public static void main(String[] args) {
        for (int i = 0; i < 512; i++) {
            String pin = String.format("%9s", Integer.toBinaryString(i)).replace(' ', '0');
            Secret secret = Secret.getInstance();
            secret.resetInstance();
            for (char c : pin.toCharArray()) {
                secret.process(c);
            }
            String data = secret.getData();
            if(data.contains("n00bz")) {
                System.out.println("PIN: " + pin + ", Flag: " + data);
            }
        }
    }
}

Ce code est conçu pour générer tous les codes PIN possibles, les convertir en binaire, et les traiter en utilisant la méthode process de la classe Secret. Il affiche ensuite le code PIN et la sortie correspondante obtenue à partir de la méthode getData() de la classe Secret, mais seulement si cette sortie contient la chaîne de caractères "n00bz".

  • Le code génère tous les nombres de 0 à 511 (soit 512 nombres au total) dans une boucle for.

  • Pour chaque nombre généré, il le convertit en binaire et le formatte pour qu’il ait une longueur de 9 caractères. Par exemple, le nombre 5 serait converti en "000000101". Le String.format est utilisé pour garantir que la longueur de la chaîne est toujours de 9 caractères, en ajoutant des zéros au début si nécessaire.

  • Il récupère ensuite une instance de la classe Secret et réinitialise l’instance.

  • Pour chaque caractère du code PIN formaté, il appelle la méthode process() de la classe Secret. Cela signifie qu’il traite chaque chiffre du code PIN un par un.

  • Il obtient ensuite la sortie en utilisant la méthode getData() de la classe Secret et la stocke dans la variable data.

  • Enfin, il vérifie si la chaîne de caractères "n00bz" est contenue dans la sortie. Si c’est le cas, il imprime le code PIN en binaire et la sortie correspondante.

En executant notre programme nous obtenons le résultat suivant:

$ javac Pin.java ; java Pin
PIN: 010110100, Flag: n00bz{y0uuu_n33d_t0_bRutefoRc3_1s_e4zyY_}   
PIN: 010110101, Flag: n00bz{y0uuu_n33d_t0_bRutefoRc3_1s_e4zyY_}

Nous obtenons le flag n00bz{y0uuu_n33d_t0_bRutefoRc3_1s_e4zyY_}