Solution de 4idenP pour Hamac

intro crypto symmetric

7 janvier 2024

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 octets
  • c : 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 !

image-1

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 :

image-4

Le flag est : FCSC{5bb0780f8af31f69b4eccf18870f493628f135045add3036f35a4e3a423976d6}.

L’épreuve est terminée.