AES NULL S-BOX Exploit (CryptoFlow)

Pour ce troisième et dernier write-up du CTF ECW édition 2023, je vais aborder la résolution d’un autre challenge de la catégorie crypto qui a l’originalité de mélanger du pwn et de la cryptographie, j’ai nommé « CryptoFlow ».

Vous pouvez télécharger le programme ici.

Découverte du challenge

Dans ce challenge nous avons accès à un binaire. On commence par le désassembler rapidement sur IDA.

Le programme effectue les actions suivantes:

  • Il charge une clé de 128 bits (16 octets) depuis un fichier « aes_ecb_key.txt » dans un buffer que j’ai nommé aes_key (fonction LoadKey).
  • Il charge le contenu d’un fichier « flag », le chiffre en utilisant de l’AES128 et affiche le contenu chiffré (fonction SendEncryptedFlag).
  • Il chiffre ensuite tout les prochains inputs qu’on lui envoie en utilisant la même clé.

On comprend rapidement que notre objectif est de déchiffrer le flag fourni au lancement du programme.

Avant de commencer, il est important de bien comprendre comment l’algorithme de chiffrement AES fonctionne pour ne pas être trop perdu. Pour cela je vous conseille cette vidéo qui présente très bien son fonctionnement.

La vulnérabilité

En jouant un peu avec le programme, on s’aperçoit qu’il y a une faille dans la fonction GetMessage. En effet, celle-ci copie toutes les données qu’on lui envoie dans un buffer situé dans le segment .data sans contrôler si l’on dépasse la taille dudit buffer (224 octets). On peut donc réaliser un buffer-overflow dans le segment .data du programme.

On s’intéresse tout naturellement au données que l’on peut overwrite dans le segment .data afin de voir ce que cette faille peut nous permettre de faire.

Les données qui suivent le buffer stockant le message à chiffrer sont les suivantes:

  • Un buffer de 16 bits intitulé « mycanary » dont le rôle est de vérifié si il y a buffer-overflow via une comparaison avec un autre buffer situé plus loin dans le segment. Seulement comme la valeur de ce canary est fixe et connue la protection n’est pas effective.
  • Après le canary on trouve un buffer de 256 bytes, nommé « s-box« . Il s’agit d’une matrice permettant le bon fonctionnement de la fonction cryptographique SubBytes dans l’algorithme AES.

On peut donc réécrire la matrice S-BOX en envoyant la payload suivante:

[224 octets][ MYCANARY = 0xAABBCCDD00112233][ S-BOX]

Mais que peut-il se passer si on change la valeur des coefficients de la matrice s-box ?

Il est important de comprendre le rôle de cette matrice au sein de l’AES. Comme dit précédemment, celle-ci assure le bon fonctionnement de la fonction SubBytes. La fonction SubBytes effectue une opération de substitution sur les coefficients d’une colonne d’une matrice. Elle associe à chaque coefficient un nouvelle valeur, suivant la règle suivante: pour une coefficient de la forme 0xAB, par exemple 0x19, elle associe la valeur se situant à la Aième ligne et à la Bième colonne, c’est à dire 0xD4 pour 0x19.

Seulement si S-BOX est nulle, alors peu importante la valeur du coefficient, sa valeur associée sera nulle. Le résultat de SubBytes sera donc toujours une matrice nulle. Cela implique qu’à la sortie de la fonction ShiftRows, qui suit SubBytes et dont le rôle est d’effectuer des opérations de permutation de ligne, aura une sortie nulle également car permuter les lignes d’une matrice nulle donnera toujours une matrice nulle. La fonction suivante, AddRoundKey, qui effectue une opération XOR terme à terme entre la matrice State et la roundkey renverra la roundkey car A xor 0 = A. Cela implique que le résultat de notre chiffrement sera égal à la Roundkey n°10 comme on peut le voir sur l’image ci-dessous.

On obtient donc après chiffrement la Roundkey n°10, la dernière des Roundkey. Et c’est très fort, car on peut retrouver la CipherKey, la clé principale, à partir d’une ième Roundkey, car les opérations les générant sont réversibles.

Revenons un peu sur le code de la fonction « main ». On s’aperçoit qu’il y a un appel à KeyExpension qui est effectué avant que l’on overwrite S-BOX. C’est un point très important, car c’est la fonction KeyExpension qui génère les 10 RoundKeys, et que celle-ci utilise la matrice S-BOX. Tous les appels à KeyExpension qui seront effectués après l’overwrite de S-BOX généreront des clés qui ont perdu l’information de la CipherKey, c’est à dire des clés inexploitables. C’est donc bien le résultat du premier message chiffré qui est égale à la 10ième RoundKey.

KeyExpension inverse

J’ai commencé par coder la fonction générant la ième Roundkey à partir de la (i-1)ème clé.

Puis j’ai codé sa fonction inverse.

Le code complet est disponible sur mon github ici (bientôt).

Il suffit maintenant de le lancer pour obtenir la CipherKey et pouvoir déchiffré le flag.


Publié

dans

, , , ,

par

Étiquettes :