M30W Vault Tech
Flag: CYBN{0n_d1t_ch1ffr3m3nt_aHahAh_c3stM4rr4nt}
Challenge
Solution
Analyse statique
Décompilation avec Ghidra. Dans l'entry
, on trouve la fonction FUN_00402800
qui est le main
void entry(undefined8 param_1,undefined8 param_2,undefined8 param_3) {
undefined8 in_stack_00000000;
undefined auStack8 [0x8];
__libc_start_main(FUN_00402800,in_stack_00000000,&stack0x00000008,0x0,0x0,param_3,auStack8);
}
J'en profite pour renommer les paramètres de main
pour la lisibilité
int main(int argc, char **argv) {}
On trouve une partie lecture du fichier, on peut en déduire le nom des variables. De plus, quand on est en mode decryption, on skip les 16 premiers octets du fichier :
// [ ... ]
// ligne 58
decryption_mode = 0;
// On déduit facilement que FUN_004023b0 compare les chaînes
if ((0x3 < argc_) && (uVar2 = FUN_004023b0(argv_[0x3],"--decrypt"), (uVar2 & 0x1) != 0x0)) {
// [...] lignes osef pour print dans le terminal
decryption_mode = 1;
}
// ligne 74
fseek(file_in, 0, 2); // Va à la fin du fichier
file_size = ftell(file_in); // Recupère l'index où nous sommes
fseek(file_in,0x0,0x0); // Retourne au début du fichier
file_content = malloc(file_size); // Alloue un buffer de la taille du fichier
fread(file_content,0x1,file_size,file_in); // Lis le contenu du fichier et le place dans le buffer
fclose(file_in);
if ((decryption_mode & 0x1) != 0x0) { // Si on est en mode decryption
file_content = (void *)((long)file_content + 0x10); // On avance le pointeur de 16 octets (donc on "skip" les 16 premiers octets)
file_size = file_size - 0x10; // Réduit la taille du fichier de 16
// [ ... ]
Génération du secret
En dessous, on peut voir quelque chose qui ressemble à de la génération de clé :
// ligne 90
pcVar4 = argv_[0x2]; // argv[2] c'est le password
lVar3 = FUN_00402370(pcVar4); // on fait un truc avec le password
local_60 = FUN_00402720(pcVar4,lVar3); // on fait un second truc avec le password et le résultat de la fonction d'avant
local_68 = FUN_00402450((long)file_content,file_size,(long)local_60); // on fait un dernier truc avec le contenu du fichier + le password
Commençons par FUN_00402370
, elle calcule la taille d'une chaîne de caractère, on la renomme en get_size
.
size_t FUN_00402370(char *param_1) {
size_t size;
char *i;
size = 0x0;
for (i = param_1; *i != '\0'; i = i + 0x1) {
size = size + 0x1;
}
return size;
}
Ensuite FUN_00402720
, elle prend donc en paramètre le password et sa taille. Elle semble générer un secret. Les 8 premiers octets sont générés par FUN_004026c0 (elle fait un sha1 des 2 premiers caractères du password mais en s'en fou au final, donc je skip ce reverse).
Les 8 caractères suivants sont un XOR du password avec la clé M30W
. On renomme cette fonction generate_secret
.
void * FUN_00402720(char *password,long size) {
uchar *puVar1;
char *secret;
uint j;
uint i;
puVar1 = FUN_004026c0(password,size);
secret = (char *)calloc(0x10,0x1);
for (i = 0x0; i < 0x8; i = i + 0x1) {
secret[i] = puVar1[i];
}
for (j = 0x0; j < 0x8; j = j + 0x1) {
secret[j + 0x8] = password[(long)(ulong)j % size] ^ "M30W"[j & 0x3];
}
return secret;
}
Avec les renommages, on se retrouve avec un code un peu plus compréhensible. On peut d'ailleurs en déduire que FUN_00402450
est la fonction de chiffrement et local_68
le résultat :
// ligne 90
password = argv_[0x2];
password_size = get_size(password);
secret = generate_secret(password,password_size);
encrypted = encrypt((long)file_content,file_size,(long)secret);
Retrouver la clé
// Ligne 117
if ((decryption_mode & 0x1) == 0x0) { // En mode chiffrement
useless_var1 = fwrite(secret,0x1,0x10,__s); // On écrit le secret au début du fichier out, il fait bien 16 octets
}
useless_var2 = fwrite(encrypted,0x1,file_size,__s); // Ajoute le résultat du chiffrement dans le fichier out
On comprend que c'est un chiffrement symétrique puisque la fonction encrypt
est utilisée dans les 2 modes.
De plus, on peut retrouver le mot de passe utilisé à partir du fichier chiffré. Il suffit de refaire le XOR sur les 8 derniers octets du secret. En python ça donne :
with open('flag.txt.inc', 'rb') as f:
flag_encrypted = f.read()
password_encrypted = flag_encrypted[8:16]
key = b'M30W'
recovered = bytes([password_encrypted[i] ^ key[i % len(key)] for i in range(len(password_encrypted))])
print(recovered.decode())
# 1H0p31W1
Déchiffrement du flag
On a le password utilisé, on va juste lancer le binaire en mode decrypt avec :
┌──(kali㉿kali)-[~/Documents/CYBN 2024]
└─$ ./m30w_vault flag.txt.inc 1H0p31W1 --decrypt
-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=--
SECURE M30W VAULT TECH
Unique,
Premium ,
State of the art,
CRYPTAGE :3
-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=--
Decrypting service selectionned
Writting plaintext to : flag.txt.inc.out
44 bytes written, enjoy
┌──(kali㉿kali)-[~/Documents/CYBN 2024]
└─$ cat flag.txt.inc.out
CYBN{0n_d1t_ch1ffr3m3nt_aHahAh_c3stM4rr4nt}
Dernière mise à jour
Cet article vous a-t-il été utile ?