Je sais pas comment t'as fait ton compte mais on dirait que tu es totalement perdu... Heureusement pour toi, si tu observes de manière méthodique le labyrinthe qui se dresse devant toi, tu devrais pouvoir t'en sortir !
Solution
Décompilation avec Ghidra. Les symboles n'ont pas été retirés pour faciliter le challenge. On commence par le challenge 1.
Challenge 1
On peut voir ligne 14 une comparaison avec Knuckles > Sonic, c'est le mot de passe attendu.
uint challenge_1(int socket) {
int iVar1;
size_t sVar2;
char local_318 [0x100];
char local_218 [0x208];
uint local_10;
int local_c;
snprintf(local_218,0x200,"Un temple sacré se dresse devant toi, seul celui qui connaît la phrase sacrée peut espérer entrer.\nMais attention, si tu te tro mpes des Ugandan Knights viendront s\'occuper de toi !\n> ");
sVar2 = strlen(local_218);
send(socket,local_218,sVar2,0x0);
memset(local_218,0x0,0x200);
local_c = read_line(socket,(long)local_318,0xff);
iVar1 = strcmp(local_318,"Knuckles > Sonic");
local_10 = (uint)(iVar1 == 0x0);
if (local_10 == 0x0) {
snprintf(local_218,0x200,"\nLes Ugandan Knights se retournent vers toi... commencent à faire des cliquetis... et attrapent leurs armes ! CAVALE KENNY, C AVALE !\n");
}
else {
snprintf(local_218,0x200,"\nLa porte du temple s\'ouvre tandis les Ugandan Knights ne font même pas attention à toi. Tu décides d\'entrer à la recherche du trésor !\n");
}
sVar2 = strlen(local_218);
send(socket,local_218,sVar2,0x0);
return local_10;
}
Challenge 2
Ligne 18, on appelle check_challenge_2 avec ce qui a été lu dans le socket et local_1c, c'est-à-dire la valeur 0x3d923435fe5b07e4.
bool challenge_2(int socket) {
size_t sVar1;
undefined8 uVar2;
char *param3;
char local_233 [0xb];
char local_228 [0x20c];
undefined8 local_1c;
int local_14;
size_t local_10;
local_1c = 0x3d923435fe5b07e4;
local_10 = 0x200;
snprintf(local_228,0x200,"\nTu as réussis à entrer dans le temple, maintenant commence le dédale infernal. Prouves que tu connais da wae pour\narriver jus qu\'au trésor. Seul un chemin de 10 mouvements à travers le labyrinthe peux te sauver, utilise les directions :\nD (Droite) | G ( Gauche)| H (Haut) | B (Bas) (ex: BGDHDBDHGB)\n> ");
sVar1 = strlen(local_228);
send(socket,local_228,sVar1,0x0);
memset(local_228,0x0,local_10);
read_line(socket,(long)local_233,0xb);
uVar2 = check_challenge_2(local_233,&local_1c);
local_14 = (int)uVar2;
if (local_14 == 0x0) {
param3 = getenv("FLAG");
snprintf(local_228,local_10,"\nGood job! Your flag is %s\n",param3);
}
else if (local_14 == 0x1) {
snprintf(local_228,local_10,"\nTu dois faire 10 mouvements, pas plus, pas moins...\n");
}
else if (local_14 == 0x2) {
snprintf(local_228,local_10,"\nDa wae est composée uniquement de D, G, H et B, tu ne la connais visiblement pas...\n");
}
else if (local_14 == 0x3) {
snprintf(local_228,local_10,"\nAïe ! Tu t\'es perdu dans le labyrinthe, you don\'t know da wae...\n");
}
sVar1 = strlen(local_228);
send(socket,local_228,sVar1,0x0);
return local_14 == 0x0;
}
Maintenant, on regarde check_challenge_2 :
undefined8 check_challenge_2(char *input,char *local_1c) {
int c;
size_t input_size;
undefined8 uVar1;
uchar local_60 [0x8];
uchar hash_result [0x27];
byte xored_input [0xa];
char key [0x7];
undefined8 local_20;
size_t key_size;
int i;
local_20 = 0xd070c0a0e040919;
input_size = strlen(input);
if (input_size == 10) { // Notre input doit faire 10 caractères.
for (i = 0x0; i < 0xa; i = i + 0x1) {
input[i] = toupper((uint)(byte)input[i]); // Met en majuscule tous les caractères de notre input
// Ici, on vérifie que notre input ne contient que des D, G, H ou B
if ((((input[i] != 'D') && (input[i] != 'G')) && (input[i] != 'H')) && (input[i] != 'B')) {
return 0x2;
}
}
key = "shadow";
key_size = strlen(key);
// XOR notre input avec la clé "shadow"
for (i = 0x0; i < 10; i = i + 0x1) {
xored_input[i] = input[i] ^ key[(ulong)(long)i % key_size];
}
// Fait un SHA256 du résultat de notre input XOR avec la clé
SHA256(xored_input,10,hash_result);
// Utilise local_20 comme tableau d'index pour ne garder que 8 octets du hash et les mettre dans local_60
for (i = 0x0; i < 0x8; i = i + 0x1) {
local_60[i] = hash_result[local_20[i]];
}
// Compare les 8 octets retenus avec local_1c que l'on a passé en paramètre à cette fonction
c = memcmp(local_60,local_1c,0x8);
if (c == 0x0) {
uVar1 = 0x0;
}
else {
uVar1 = 0x3;
}
}
else {
uVar1 = 0x1;
}
return uVar1;
}
On comprend donc que local_1c est le résultat attendu des octets conservés du hash. Comme on connait cette valeur et qu'il n'y a que 4 caractères possibles répétés 10 fois (4 puissance 10 = 1048576 possibilités), on peut bruteforce localement.
En python ça donne :
from hashlib import sha256
from itertools import product
key = b'shadow'
key_size = len(key)
local_1c = 0x3d923435fe5b07e4.to_bytes(8, 'little')
local_20 = 0xd070c0a0e040919.to_bytes(8, 'little')
for o in product(b'DGHB', repeat=10):
path = bytes(o)
xor_path = bytes([path[i] ^ key[i % key_size] for i in range(10)])
hash_ = sha256(xor_path).digest()
bytes_retained = bytes([hash_[local_20[i]] for i in range(8)])
if bytes_retained == local_1c:
print(f'Good path: {path.decode()}')
break
# Good path: HHBBGDGDHB