FCSC 2021 - Intro - La PIN

Table des matières :

J’ai protégé le flag en le chiffrant avec des algorithmes modernes. Pourrez-vous le retrouver ?

Fichiers joints :


Résolution

Tout d’abord, lisons le script python lapin.py ayant servi à chiffrer le flag. Dans ce script nous remarquons un bloc permettant d’entrer un code pin compris entre 0 et 9999 :

while True:
	pin = int(input(">>> PIN code (4 digits): "))
	if 0 < pin < 9999:
		break

La seconde partie gère le chiffrement du flag. La clé k de chiffrement est générée grâce à une fonction de génération de clés scrypt ne dépendant que du PIN entré plus haut.

flag = open("flag.txt", "rb").read()
k = scrypt(long_to_bytes(pin), b"FCSC", 32, N = 2 ** 10, r = 8, p = 1)

Ensuite le flag est chiffré avec la clé k sur AES en mode GCM et le message chiffré est sauvegardé sous format hexadécimal.

aes = AES.new(k, AES.MODE_GCM)
c, tag = aes.encrypt_and_digest(flag)

enc = aes.nonce + c + tag
print(enc.hex())

Maintenant que nous avons une idée de la manière dont le flag est chiffré, nous pouvons réfléchir à une attaque. La première idée qui vient est d’effectuer un bruteforce du PIN. En effet comme celui-ci est constitué de seulement 4 chiffres, le bruteforce sera très rapide.

Avant d’effectuer le bruteforce, nous devons lire le flag chiffré et extraire le nonce de 16 bytes au début et le tag de 16 bytes à la fin. Ensuite nous effectuons une boucle de 0 à 9999 pour généréer toutes les clés possible avec la fonction scrypt et nous tentons de déchiffrer et vérifier le message chiffré. Voici tout cela combiné dans un script python :

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import binascii
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import scrypt
from Crypto.Util.number import long_to_bytes

data = binascii.unhexlify("f049de59cbdc9189170787b20b24f7426ccb9515e8b0250f3fc0f0c14ed7bb1d4b42c09d02fe01e0973a7233d99af55ce696f599050142759adc26796d64e0d6035f2fc39d2edb8a0797a9e45ae4cd55074cf99158d3a64dc70a7e836e3b30382df30de49ba60a")
nonce, encrypted, tag = data[:16], data[16:-16], data[-16:]

print(' Nonce : %s' % nonce)
print(' tag   : %s' % tag)

for pin in range(0,9999):
	print('\r[>] Trying PIN %04d' % pin,end="")
	key = scrypt(long_to_bytes(pin), b"FCSC", 32, N = 2 ** 10, r = 8, p = 1)
	try:
		cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
		print(cipher.decrypt_and_verify(encrypted, tag))
	except Exception as e:
		pass

Il ne reste plus qu’a lancer le script, et 2 minutes plus tard :

$ ./bf.py
 Nonce : b'\xf0I\xdeY\xcb\xdc\x91\x89\x17\x07\x87\xb2\x0b$\xf7B'
 tag   : b'M\xc7\n~\x83n;08-\xf3\r\xe4\x9b\xa6\n'
[>] Trying PIN 6273
 b'FCSC{c1feab88e6c6932c57fbaf0c1ff6c32e51f07ae87197fcd08956be4408b2c802}\n'
[>] Trying PIN 9998

Et nous obtenons le flag :

FCSC{c1feab88e6c6932c57fbaf0c1ff6c32e51f07ae87197fcd08956be4408b2c802}