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"
. LeString.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 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 classeSecret
et 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_}