The Ugandan Labyrinth
Flag: CYBN{d4_w4y_1s_n0t_th3_34s13st_but_th3_funn13st}
Challenge
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
Obtention du flag
Il suffit de se connecter et envoyer ces infos

Dernière mise à jour
Cet article vous a-t-il été utile ?