Les attaques par extension de longueur permettent à un attaquant de forger un Hash(Clé secrète||Donnée légitime||Donnée malveillante) à partir d’un Hash(Clé secrète||Donnée légitime) et de la longueur connue de (Clé secrète||Donnée légitime). L’attaque utilise le condensat de sortie légitime pour reconstruire l’état interne de la fonction de hachage. À partir de là, il est trivial d’alimenter la fonction de hachage avec la donnée malveillante à ajouter puis de produire un nouveau condensat tout aussi valide. Les fonctions de hachage cryptographiques vulnérables sont celles basées sur la construction de Merkle-Damgård telles que MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512. Cependant SHA-512/224 et SHA-512/256 ne sont pas touchées puisque leur condensat final en sortie est tronqué rendant très difficile la reconstitution de leur état interne. En effet, il s’avère que 100% de l’état nécessaire pour poursuivre un hachage se trouve dans le condensat complet et non tronqué de ces algorithmes. Autrement dit, étant donné un condensat composé d’une chaîne de caractères avec un préfixe inconnu faisant office de clé secrète, un attaquant peut concaténer à cette chaîne une nouvelle chaîne puis recalculer un nouveau condensat qui contient toujours le préfixe inconnu. Ainsi, un condensat SHA-2 utilisé comme Message Authentication Code est une copie complète de l’état du hachage du préfixe, de sa donnée et de son padding. Passons à la pratique avec un exemple concret issu du billet de blog de Sylvain Kerkour, Breaking SHA-256: length extension attacks in practice .

Contexte

Imaginons qu’une application web intègre un mécanisme d’authentification “sans état” basé sur un jeton. Il permet d’accéder à une ressource spécifique sans avoir à transmettre à chaque requête le nom d’utilisateur et le mot de passe associé. “Sans état” signifie ici que l’application web n’a pas besoin de conserver l’état de la connexion de l’utilisateur (connecté ou non connecté) puisque la transmission du jeton est effectuée à chaque interrogation. Le demandeur sera directement authentifié unitairement si son token est validé par l’application.

L’application génère une clé secrète par utilisateur qu’elle seule connait, disons AliceBobForever pour Alice (oui, son entropie est très mauvaise mais c’est pour l’exemple !). Le jeton d’authentification a cette forme : Token = Donnée légitime|SHA-256(Clé secrète||Donnée légitime) où la donnée utile à l’authentification d’Alice est hachée puis transmise à l’application afin qu’elle vérifie son authenticité. Ce jeton est envoyé à Alice après une première authentification auprès de l’application avec son couple identifiant/mot de passe.

Ainsi le jeton que reçoit Alice a cette forme : Token = user_id=1&role=user|SHA-256(AliceBobForever||user_id=1&role=user). La donnée légitime est constituée des paramètres user_id=1&role=user puisqu’ils sont nécessaires à l’application pour identifier Alice. Par conséquent, le jeton qu’elle reçoit est : Token = user_id=1&role=user|45fcec6060b47fce910985eabfc0ca6d8d12674ee7f4e4c4f59d3b6268b6302f.

Comme expliqué dans le billet Présentation des fonctions de hachage SHA-2 , le Message block légitime est ainsi constitué :

01000001 01101100 01101001 01100011
01100101 01000010 01101111 01100010
01000110 01101111 01110010 01100101
01110110 01100101 01110010 01110101
01110011 01100101 01110010 01011111
01101001 01100100 00111101 00110001
00100110 01110010 01101111 01101100
01100101 00111101 01110101 01110011
01100101 01110010 10000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000001 00010000

En le décomposant, on retrouve :

  • 34 octets (15 + 19) de donnée légitime (Clé secrète||Donnée légitime) soit 272 bits,
  • un octet à 0x80 séparant la donnée légitime des octets à 0x00,
  • 21 octets à 0x00,
  • le nombre 272 codé sur 64 bits soit 0b0000000000000000000000000000000000000000000000000000000100010000,
  • un padding total de 30 octets.

Attaque

Eve aimerait se faire passer pour Alice tout en obtenant des droits d’administration. Pour cela, elle tente de forger un jeton malveillant mais valide d’après l’application web. Faisons un point d’étape sur ce que Eve connait :

  • La longueur de la clé secrète (par supposition, après plusieurs tentatives avec des jetons forgés voire après la lecture des éventuelles spécifications applicatives…),
  • La donnée légitime composée des paramètres user_id=1&role=user,
  • Indirectement le padding de la requête légitime puisqu’elle peut le reconstituer,
  • La donnée malveillante composée du paramètre &role=admin,
  • Le condensat de la requête légitime afin de reconstruire l’état interne de la fonction de hachage cryptographique.

Le token forgé prendra cette forme : Token = user_id=1&role=user||padding du Message block légitime||&role=admin|SHA-256(user_id=1&role=user||padding du Message block légitime||&role=admin) soit Token = user_id=1&role=user\0x80\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x00\0x01\0x10&role=admin|105ad32aee8092859fc94569caf8e8ae45f61f78d7b2ba978cc011ac6254a09d ou encore en passant l’ensemble en hexadécimal : Token = 757365725f69643d3126726f6c653d7573657280000000000000000000000000000000000000000000000000000000011026726f6c653d61646d696e|105ad32aee8092859fc94569caf8e8ae45f61f78d7b2ba978cc011ac6254a09d.

Pour arriver à ce nouveau condensat, Eve doit créer une implémentation modifiée de SHA-256 dont l’état de départ est l’état final de SHA-256(AliceBobForever||user_id=1&role=user), c’est-à-dire 45fcec6060b47fce910985eabfc0ca6d8d12674ee7f4e4c4f59d3b6268b6302f. Une fois cet état atteint, il suffit de continuer le calcul du condensat de &role=admin. Récupérons donc l’état final dans des variables :

h0 = 0x45fcec60
h1 = 0x60b47fce
h2 = 0x910985ea
h3 = 0xbfc0ca6d
h4 = 0x8d12674e
h5 = 0xe7f4e4c4
h6 = 0xf59d3b62
h7 = 0x68b6302f

Ce que l’on a fait ici, c’est modifier les constantes d’initialisation nécessaires à SHA-256 ainsi l’implémentation modifiée part d’un autre état que celui prévu dans le RFC-6234 .

Vérification côté application

Eve envoie donc à l’application le Token = 757365725f69643d3126726f6c653d7573657280000000000000000000000000000000000000000000000000000000011026726f6c653d61646d696e|105ad32aee8092859fc94569caf8e8ae45f61f78d7b2ba978cc011ac6254a09d qui va vérifier que ce condensat est égal à SHA-256(AliceBobForever||user_id=1&role=user||padding du Message block légitime||&role=admin) ou plus précisement SHA-256(416C696365426F62466F7265766572757365725f69643d3126726f6c653d7573657280000000000000000000000000000000000000000000000000000000011026726f6c653d61646d696e). S’ils sont égaux, et c’est le cas ici, Eve deviendra administratrice !

Conclusion

Lorsqu’on intègre un mécanisme de vérification d’authenticité et d’intégrité par Message Authentication Code, il est impératif de se passer des fonctions de hachage cryptographiques vulnérables aux attaques par extension de longueur. On leur préférera donc les fonctions HMAC-SHA-256, HMAC-SHA-512, SHA-3 ou KMAC selon le besoin métier.

Outillage