Aller au contenu principal

Cbizarre [2/2]

Description du challenge

  • Nom du CTF : 404CTF 2025
  • Catégorie : Reverse
  • Difficulté : Intro
  • Date : Mai 2025

On nous donne le challenge suivant :

Chall

Analyse du binaire

Le fichier que nous téléchargeons est un binaire Linux :

$ file chall2
chall2: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=683fe4c494b6b7dc5df519f9299a42f6616677ff, for GNU/Linux 3.2.0, not stripped

On peut faire une première analyse avec strings pour voir les chaînes de caractères présentes dans le binaire :

$ strings chall2

Quelques chaînes intéressantes apparaissent :

Usage: %s <password>
Error: Incorrect password.
Bravo ! Vous avez le flag ! %s

Je décide de lancer Ghidra pour extraire le code C du binaire.

undefined8 main(int param_1,undefined8 *param_2)

{
undefined8 uVar1;
size_t sVar2;
undefined8 local_28;
undefined5 local_20;
undefined3 uStack_1b;
undefined5 uStack_18;
undefined8 local_10;

if (param_1 == 2) {
sVar2 = strlen((char *)param_2[1]);
if (sVar2 == 0x14) {
if (*(char *)(param_2[1] + 5) != 'Z') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 0xc) != 'o') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)param_2[1] != 'f') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 0x12) != '1') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 7) != '%') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 3) != 'M') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 9) != 'y') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 0x10) != 'v') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 0xe) != 'n') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 1) != 'a') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 0x13) != 'x') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 6) != 'a') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 0xf) != 'M') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 8) != '3') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 4) != 'P') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 0xb) != 'K') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 10) != 'N') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 0x11) != '%') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 2) != 'V') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
if (*(char *)(param_2[1] + 0xd) != '@') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}
local_28 = 0x661a1c040e625152;
local_20 = 0x492f7e4954;
uStack_1b = 0x200233;
uStack_18 = 0x5026906;
local_10 = xor(&local_28,param_2[1],0x14);
printf("Bravo ! Vous avez le flag ! %s\n",local_10);
uVar1 = 0;
}
else {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
uVar1 = 1;
}
}
else {
fprintf(stderr,"Usage: %s <password>\n",*param_2);
uVar1 = 1;
}
return uVar1;
}

Pour obtenir le flag, il faut que le mot de passe fasse 20 caractères. Il vérifie aussi que chaque caractère du mot de passe est présent à une position précise :

if (*(char *)(param_2[1] + 5) != 'Z') {
fwrite("Error: Incorrect password.\n",1,0x1b,stderr);
// WARNING: Subroutine does not return
exit(1);
}

Ici, le sixième caractère est un Z.

Le mot de passe voulu est alors : faVMPZa%3yNKo@nMv%1x.

Si c'est le cas, on initialise des valeurs héxadécimales qui seront utilisées pour le XOR avec le mot de passe.

Récupération du flag

On définit la fonction Python suivante pour récupérer le flag :

def extract_flag():
# Mot de passe reconstruit à partir des vérifications du code C
password = [' '] * 20

password[0] = 'f'
password[1] = 'a'
password[2] = 'V'
password[3] = 'M'
password[4] = 'P'
password[5] = 'Z'
password[6] = 'a'
password[7] = '%'
password[8] = '3'
password[9] = 'y'
password[10] = 'N'
password[11] = 'K'
password[12] = 'o'
password[13] = '@'
password[14] = 'n'
password[15] = 'M'
password[16] = 'v'
password[17] = '%'
password[18] = '1'
password[19] = 'x'

# Constantes hexadécimales définies dans le code C
key_parts = [
0x661a1c040e625152,
0x492f7e4954,
0x200233,
0x5026906
]

# Convertir les constantes hexadécimales en bytes
key_bytes = b''
for part in key_parts:
# Convertir chaque partie en bytes, little-endian
part_bytes = part.to_bytes((part.bit_length() + 7) // 8, byteorder='little')
key_bytes += part_bytes

key_bytes = key_bytes[:20]

# Fonction XOR comme implémentée dans le code C
flag = ''
for i in range(len(password)):
flag += chr(ord(password[i]) ^ key_bytes[i])

return flag

if __name__ == "__main__":
flag = extract_flag()
print(f"Flag: {flag}")
FLAG

404CTF{Cg00d&s1mpL3}