Passer au contenu

Hunting License

Résumé

InfoValeur
VulnérabilitéTrois protections par mot de passe avec des mécanismes faibles
TechniqueTexte clair, chaîne inversée, et déchiffrement XOR
Outilsfile, strings, radare2/Ghidra, Python

Reconnaissance initiale

Identification du fichier

Fenêtre du terminal
$ file license
license: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
for GNU/Linux 3.2.0, not stripped

Points notables :

  • not stripped : Les noms des fonctions sont visibles
  • dynamically linked : Utilise les bibliothèques partagées

Exécution du programme

Fenêtre du terminal
$ ./license
What is the password? test
Wrong!

Le programme demande un mot de passe. En fait, il y a trois niveaux de protection successifs.


Première protection : texte clair

Analyse avec strings

Fenêtre du terminal
$ strings license | grep -i password
What is the password?

Analyse dans radare2

Fenêtre du terminal
$ r2 -A license
[0x00401060]> afl # Lister les fonctions
...
0x00401172 1 107 sym.exam
0x004011dd 1 91 sym.reverse
0x00401238 1 72 sym.xor
...

Trois fonctions intéressantes sont identifiées : exam, reverse, et xor.

Fonction exam

Fenêtre du terminal
[0x00401060]> pdf @sym.exam

La fonction exam contient la première vérification :

; Première question
lea rdi, str.What_is_the_password?
call sym.imp.printf
lea rdi, [rbp-0x20]
call sym.imp.scanf
lea rdi, str.**REDACTED** ; La clé en clair !
lea rsi, [rbp-0x20]
call sym.imp.strcmp
test eax, eax
jne wrong

Premier mot de passe : **REDACTED**

La chaîne est visible directement dans le binaire, comparée avec strcmp().

Fenêtre du terminal
$ ./license
What is the password? **REDACTED**
Correct!
...

Deuxième protection : chaîne inversée

Analyse de la fonction reverse

La deuxième vérification utilise la fonction reverse :

// Pseudo-code de la fonction reverse
char* reverse(char *str) {
int len = strlen(str);
char *reversed = malloc(len + 1);
for (int i = 0; i < len; i++) {
reversed[i] = str[len - 1 - i];
}
reversed[len] = '\0';
return reversed;
}

Trouver la chaîne source

Dans la fonction exam, après la première vérification :

; Deuxième question
lea rdi, str.What_is_the_second_password?
call sym.imp.printf
lea rdi, [rbp-0x20]
call sym.imp.scanf
lea rdi, str.0wTdr0wss worNtworthy ; Chaîne à inverser
call sym.reverse ; Inversion
lea rsi, [rbp-0x20]
call sym.imp.strcmp

La chaîne stockée dans le binaire est inversée avant la comparaison. Il faut donc l’inverser nous-mêmes :

>>> s = "0wTdr0wss worNtworthy"[::-1]
>>> print(s)
yhtrrowTNrow sswOrdTw0

Mais attention, la chaîne exacte dépend de ce qui est stocké dans le binaire. Analysons avec radare2 :

Fenêtre du terminal
[0x00401060]> ps @0x402040 # Afficher la chaîne à l'adresse mémoire

Deuxième mot de passe : la chaîne inversée extraite du binaire.


Troisième protection : chiffrement XOR

Analyse de la fonction xor

// Pseudo-code de la fonction xor
char* xor_decrypt(char *data, int key) {
int len = strlen(data);
char *result = malloc(len + 1);
for (int i = 0; i < len; i++) {
result[i] = data[i] ^ key;
}
result[len] = '\0';
return result;
}

Extraction des données chiffrées et de la clé

Dans la troisième vérification :

; Troisième question
lea rdi, str.What_is_the_third_password?
call sym.imp.printf
lea rdi, [rbp-0x20]
call sym.imp.scanf
lea rdi, [encrypted_data] ; Données chiffrées
mov esi, 0x13 ; Clé XOR = 0x13
call sym.xor ; Déchiffrement XOR
lea rsi, [rbp-0x20]
call sym.imp.strcmp

Script de déchiffrement

#!/usr/bin/env python3
"""
Déchiffrement XOR pour le troisième mot de passe
"""
# Données chiffrées extraites du binaire (à l'adresse mémoire identifiée)
encrypted = [
# Octets extraits depuis radare2/Ghidra
# Exemple : 0x55, 0x64, 0x78, ...
]
key = 0x13 # Clé XOR extraite du code assembleur
decrypted = ''.join(chr(b ^ key) for b in encrypted)
print(f"Troisième mot de passe : {decrypted}")

Tutoriel radare2

Commandes essentielles utilisées

CommandeDescription
r2 -A binaryOuvrir avec analyse automatique
aflLister toutes les fonctions
pdf @sym.funcDésassembler une fonction
ps @addrAfficher une chaîne à une adresse
px N @addrHexdump de N octets à une adresse
VV @sym.funcVue graphique d’une fonction
izLister les chaînes du binaire
axt @addrTrouver les références croisées vers une adresse

Workflow typique dans radare2

Fenêtre du terminal
$ r2 -A license
# 1. Lister les fonctions
[0x00401060]> afl
# 2. Analyser la fonction principale
[0x00401060]> pdf @sym.exam
# 3. Lister les chaînes
[0x00401060]> iz
# 4. Examiner une adresse mémoire
[0x00401060]> px 32 @0x402040
# 5. Vue graphique (très utile pour comprendre les branches)
[0x00401060]> VV @sym.exam

Rappels sur l’assembleur x86-64

Instructions courantes rencontrées

InstructionDescriptionExemple
movCopie une valeurmov rax, 5
leaCharge une adresselea rdi, [str]
callAppelle une fonctioncall strcmp
cmpCompare deux valeurscmp eax, 0
testET logique (met les flags)test eax, eax
je/jneSaut conditionneljne wrong
push/popPilepush rbp

Registres pour les arguments (convention System V AMD64)

1er argument → RDI
2e argument → RSI
3e argument → RDX
4e argument → RCX
5e argument → R8
6e argument → R9
Retour → RAX

Exemple concret

lea rdi, str.password ; 1er arg de strcmp = "password"
lea rsi, [rbp-0x20] ; 2e arg de strcmp = entrée utilisateur
call sym.imp.strcmp ; strcmp(password, input)
test eax, eax ; eax == 0 ? (chaînes identiques ?)
jne wrong ; Si non, sauter à "wrong"

Exploitation complète

Fenêtre du terminal
$ nc REMOTE_IP REMOTE_PORT
What is the password? **REDACTED**
Correct!
What is the second password? <chaîne_inversée>
Correct!
What is the third password? <chaîne_déchiffrée_xor>
Correct!
HTB{XXXXXXXXXXXXXXXXXXXXXXX}

Concepts appris

Trois niveaux de protection

NiveauMécanismeFaiblesse
1strcmp() avec texte clairVisible avec strings
2strcmp() après reverse()Inversible trivialement
3strcmp() après xor()Clé XOR visible dans le code

Chiffrement XOR

Chiffrement : texte ^ clé = chiffré
Déchiffrement : chiffré ^ clé = texte
Propriété : A ^ B ^ B = A

Inversion de chaîne

# En Python, inverser une chaîne :
original = "Hello"
reversed_str = original[::-1] # "olleH"

Méthodologie

1. file / strings → Identifier le binaire et chercher des indices
2. r2 -A / Ghidra → Analyser les fonctions (exam, reverse, xor)
3. Password 1 → strcmp avec texte clair → strings suffit
4. Password 2 → strcmp après reverse() → inverser la chaîne
5. Password 3 → strcmp après xor() → déchiffrer avec la clé
6. Connecter → Envoyer les 3 mots de passe pour obtenir le flag