Skip to main content

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}