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
5serait converti en"000000101". LeString.formatest 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
Secretet réinitialise l’instance. -
Pour chaque caractère du code PIN formaté, il appelle la méthode
process()de la classeSecret. 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 classeSecretet la stocke dans la variabledata. -
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_}