Solution de GarlicHorse pour Clair connu

intro crypto

30 juillet 2024

Ce challenge nous propose un chiffrement à masque jetable qui est une méthode de chiffrement jugée parfaite. Nous disposons du chiffré en pièce jointe au format hexadécimal.


L’opération XOR est une opération logique fondamentale en informatique qui suit la table de vérité suivante :

$$\begin{array}{c|c|c} A & B & A \oplus B \\ \hline 0 & 0 & 0 \\ 0 & 1 & 1 \\ 1 & 0 & 1 \\ 1 & 1 & 0 \\ \end{array}$$

Propriétés de l’opération XOR

  • Commutativité : $A \oplus B = B \oplus A$

  • Associativité : $(A \oplus B) \oplus C = A \oplus (B \oplus C)$


Pour résoudre ce challenge, il faut prêter attention à la génération de la clé :

key = os.urandom(4) * 20

La fonction urandom est une fonction générant de l’aléa avec une entropie plus forte que random donc une méthode statistique est très peu problable pour ce challenge. Cette fonction urandom génère des octets, ici 4, puis on concatène 20 fois cette séquence de 4 octets pour obtenir la clé.

Une méthode possible ici est le bruteforce : en effet 2^32 est possible sur nos ordinateurs modernes en un temps raisonnable mais un challenge CTF ne demanderait pas ça.

Pour résoudre le challenge, on remarque que l’on connaît le format du flag : FCSC{____________}. Un charactère est encodé sur un octet donc en utilisant les propriétés de l’opérateur xor on a :

$Chiffre = cle(0)\oplus F || cle(1)\oplus C || cle(2)\oplus S || cle(3)\oplus C || … $ , $cle(i)$ est le $i$-ème octet de la clé et || désigne la concaténation.

On retrouve ainsi la clé entière en concaténant 20 fois $cle(0)\oplus F || cle(1)\oplus C || cle(2)\oplus S || cle(3)$.

Il suffit alors de XOR la clé avec le chiffré pour retrouver le flag.

from Crypto.Util.number import long_to_bytes
from Crypto.Util.strxor import strxor
import binascii


chiffre = binascii.unhexlify("d91b7023e46b4602f93a1202a7601304a7681103fd611502fa684102ad6d1506ab6a1059fc6a1459a8691051af3b4706fb691b54ad681b53f93a4651a93a1001ad3c4006a825")

clair_connu = b'FCSC'

clef = strxor(clair_connu, chiffre[:4])*20

print(f"Le flag est : {strxor(clef[:len(chiffre)],chiffre).decode()}.")
Le flag est : FCSC{3ebfb1b880d802cb96be0bb256f4239c27971310cdfd1842083fbe16b3a2dcf7}.