Introduction
Nous allons à travers cette solution résoudre l’épreuve Hamac
du FCSC 2022 en réalisant une attaque par dictionnaire via hashcat
.
Reconnaissance
Il nous sont fournis deux fichiers pour débuter cette épreuve, un script python hamac.py
:
# python3 -m pip install pycryptodome
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Hash import HMAC, SHA256
from Crypto.Random import get_random_bytes
print("Enter your password")
password = input(">>> ").encode()
h = HMAC.new(password, digestmod = SHA256)
h.update(b"FCSC2022")
iv = get_random_bytes(16)
k = SHA256.new(password).digest()
c = AES.new(k, AES.MODE_CBC, iv = iv).encrypt(pad(open("flag.txt", "rb").read(), 16))
r = {
"iv": iv.hex(),
"c": c.hex(),
"h": h.hexdigest(),
}
open("output.txt", "w").write(json.dumps(r))
Et un fichier texte output.txt
:
{"iv": "ea425b2ea4bb67445abe967e3bd1b583", "c": "69771c85e2362a35eb0157497e9e2d17858bf11492e003c4aa8ce1b76d8d3a31ccc3412ec6e619e7996190d8693299fc3873e1e6a96bcc1fe67abdf5175c753c09128fd1eb2f2f15bd07b12c5bfc2933", "h": "951bd9d2caae0d9e9a5665b4fc112809aac9f5f9ecbcfc5ad8e23cb1d020201d"}
A premier vue, le script hamac.py
a servi à générer le contenu du fichier output.txt
à l’aide d’un mot de passe qui nous est inconnu.
Avant de commencer cette épreuve, il nous est indiqué en commentaire qu’il va nous falloir installer
pycryptodome
afin de bénéficier des algorithmes de chiffrement et des fonctions de hashage utilisés dans ce script.
Après lecture du programme, nous savons donc à quoi correspondent les éléments de output.txt
:
iv
: Un vecteur d’initialisation de 16 octetsc
: Le flag chiffré avec AES grâce à une clék
h
: Un hash chiffré avec HMAC-SHA256
Nous chercherons donc dans un premier temps la clé k
qui a permis de chiffrer le flag avec AES.
Hors à sa déclaration, k
est calculée à partir d’une variable password
entrée par le détenteur de la clé et chiffrée en SHA256 :
k = SHA256.new(password).digest()
Ainsi notre but premier sera de trouver la valeur de ce mot de passe pour pouvoir retrouver la clé et déchiffrer le drapeau !
Crackage du mot de passe
Approche technique
Une indication très précieuse nous est donnée dans la description de cette épreuve :
Connaissez-vous l’existence de rockyou ?
Rockyou.txt
étant un célèbre dictionnaire de mots de passe, nous comprenons donc que nous allons sans doute devoir effectuer une attaque par dictionnaire… Mais sur quoi ?
Regardons le code de plus près :
print("Enter your password")
password = input(">>> ").encode()
h = HMAC.new(password, digestmod = SHA256)
h.update(b"FCSC2022")
Voilà notre porte d’entrée ! Après la saisie du mot de passe par l’utilisateur, le programme crée un nouvelle objet HMAC-SHA256
dont le mot de passe est la clé, avant de définir le message à chiffrer comme étant FCSC2022
.
Cassage du mot de passe avec hashcat et rockyou.txt
Connaissant le hash de sortie, nous allons pouvoir lancer notre attaque par dictionnaire et croiser les doigts pour que ça marche.
Nous tapons la commande suivante pour lancer l’attaque :
hashcat -m 1450 -a 0 "951bd9d2caae0d9e9a5665b4fc112809aac9f5f9ecbcfc5ad8e23cb1d020201d:FCSC2022" rockyou.txt
Les paramètres sont les suivants :
-m 1450
: Pour un hash chiffré avec HMAC-SHA256-a 0
: Pour une attaque par dictionnaire"951bd9d2caae0d9e9a5665b4fc112809aac9f5f9ecbcfc5ad8e23cb1d020201d:FCSC2022
: Pour le hash suivi du message chiffrérockyou.txt
: Le dictionnaire (situé dans /usr/share/wordlists/rockyou.txt.gz sur kali)
Nous lançons l’attaque et… bingo !
Au bout de quelques secondes seulement notre mot de passe est trouvé : omgh4xx0r.
Déchiffrement du hash AES
Pour le déchiffrement, rien de plus simple pycryptodome
nous fournit tout ce qu’il faut, il ne nous rest qu’à écrire un petit script python et le tour est joué :
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto.Util.Padding import unpad
iv = bytes.fromhex("ea425b2ea4bb67445abe967e3bd1b583")
hash = bytes.fromhex("69771c85e2362a35eb0157497e9e2d17858bf11492e003c4aa8ce1b76d8d3a31ccc3412ec6e619e7996190d8693299fc3873e1e6a96bcc1fe67abdf5175c753c09128fd1eb2f2f15bd07b12c5bfc2933")
password = "omgh4xx0r"
key = SHA256.new(password.encode()).digest()
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
plain = unpad(cipher.decrypt(hash), AES.block_size)
print(plain)
Nous chiffrons le mot de passe omgh4xx0r
en SHA-256 pour créer la clé puis nous utilisons cette même clé, combinée avec le vecteur d’initialisation fourni, pour déchiffrer le hash du drapeau :
Le flag est : FCSC{5bb0780f8af31f69b4eccf18870f493628f135045add3036f35a4e3a423976d6}
.
L’épreuve est terminée.