
🔪 PoC de l'attaque CRIME : un oracle de compression attaque CVE-2012-4929 🔪
Attaque CRIME : une attaque par oracle de compression CVE-2012-4929 découverte par Juliano Rizzo et Thai Duong ;
Dans une attaque par oracle de compression, l'utilisation d'une compression de données adaptative sur un mélange de texte clair choisi et de texte clair inconnu peut entraîner des changements sensibles au contenu dans la longueur du texte compressé, qui peuvent être détectés même si le contenu du texte compressé lui-même est ensuite chiffré. Cela peut être utilisé dans des attaques de protocole pour détecter quand le texte clair connu injecté est ne serait-ce que partiellement similaire au contenu inconnu d'une partie secrète du message, réduisant considérablement la complexité d'une recherche de correspondance pour le texte secret. Les attaques CRIME et BREACH sont des exemples d'attaques de protocole utilisant ce phénomène.
L'attaque CRIME permet de récupérer les données chiffrées envoyées par un client à un serveur en utilisant la longueur des données chiffrées. Elle ne permet pas de récupérer la clé privée utilisée pour chiffrer le message ou la requête HTTP.
De nombreux articles expliquent comment fonctionne l'attaque CRIME, mais voici les meilleures explications que j'ai trouvées sur Internet :
Cette attaque n'est vraiment pas complexe, la partie vraiment intéressante est l'implémentation qui est un peu différente de la 'théorie'.
Vérifions la méthode naïve telle que décrite dans l'article, c'est un bon moyen de comprendre comment cela fonctionne :
L'attaquant peut contrôler les requêtes envoyées par le client (en utilisant JavaScript par exemple). L'objectif est de récupérer le cookie secret. L'attaquant envoie plusieurs requêtes comme celle-ci et vérifie la longueur des données chiffrées :
| Requête | longueur |
|---|---|
| GET /cookie= DATA cookie=quokkalight | 80 |
| GET /cookie=a DATA cookie=quokkalight | 81 |
| GET /cookie=b DATA cookie=quokkalight | 81 |
| GET /cookie=. DATA cookie=quokkalight | 81 |
| GET /cookie=q DATA cookie=quokkalight | 80 |
Comme cookie=q correspond à cookie=quokkalight du cookie secret, la longueur des données chiffrées sera la même et l'attaquant sait qu'il a trouvé un octet.
Mais cette méthode échoue parfois et ne peut pas être fiable, alors nous allons plutôt utiliser une autre méthode.
D'abord, nous envoyons une requête avec le caractère que nous voulons trouver suivi de plusieurs caractères qui ne peuvent pas être trouvés dans la requête initiale, comme certains caractères spéciaux :chr(i) + "#:/[@/&". Ensuite, nous envoyons une seconde requête mais nous inversons la charge utile comme ceci : "#:/[@/&" + chr(i) et nous comparons les deux longueurs. Si len(enc(req1)) < len(enc(req2)) alors nous avons trouvé un octet. Cette méthode s'appelle : two_tries et elle est beaucoup plus fiable :
| Requête pour récupérer l'octet q | longueur |
|---|---|
| GET /cookie=a~#:/[@/& DATA cookie=quokkalight | 81 |
| GET /cookie=~#:/[@/&a DATA cookie=quokkalight | 81 |
| GET /cookie=b~#:/[@/& DATA cookie=quokkalight | 81 |
| GET /cookie=~#:/[@/&b DATA cookie=quokkalight | 81 |
| GET /cookie=q~#:/[@/& DATA cookie=quokkalight | 80 |
| GET /cookie=~#:/[@/&q DATA cookie=quokkalight | 81 |
L'attaquant a trouvé un octet !
if len(enc(request1)) < len(enc(request2)):
print("found byte")
La méthode two_tries que j'ai implémentée est entièrement récursive, mais pourquoi ? Parfois, plus d'un octet peut être trouvé car la compression correspond à plusieurs motifs.
Prenons le secret : cookie=quokkalight, si nous exécutons l'algorithme two_tries, nous trouverons le résultat suivant :
result 1: cookie=quokie=quokie=quokie=quokie=quokie=
result 2: cookie=quokkalight
L'algorithme doit emprunter tous les chemins de l'arbre afin de trouver toutes les solutions possibles. Nous pouvons voir tous les résultats comme un arbre qui peut être représenté ainsi :

La preuve de concept de l'attaque CRIME contre le mode de chiffrement de flux se trouve dans le fichier : CRIME-RC4-poc.py. C'est une implémentation en Python de l'explication précédente.
python3 CRIME-RC4-poc.py
Démo complète et résultat :
Lorsque le mode de chiffrement CBC est utilisé avec AES ou DES, l'attaque n'est pas aussi simple qu'avec RC4. Comme tout est divisé en blocs, l'attaque est un peu plus délicate (mais pas trop).
Par exemple, disons que nous utilisons AES avec le mode de chiffrement CBC, le bloc sera divisé avec une longueur de 16 et un padding sera ajouté à la fin si len(data)%16 != 0.
Dans le mode CBC, il est important de noter que la charge utile payload=rand produira une longueur de 16 et non de 12, car un padding sera ajouté à la fin des données avant le chiffrement. Notre attaque précédente ne peut donc pas fonctionner.
Exemple :
| bloc1 | bloc2 | bloc3 | longueur |
|---|---|---|---|
| GET /cookie= DA | TA cookie=quokka | light + PAD(11) | 48 |
| GET /cookie=a DA | TA cookie=quokka | light + PAD(10) | 48 |
| GET /cookie=b DA | TA cookie=quokka | light + PAD(10) | 48 |
| GET /cookie=. DA | TA cookie=quokka | light + PAD(10) | 48 |
| GET /cookie=q DA | TA cookie=quokka | light + PAD(11) | 48 |
Dans cet exemple, la longueur sera toujours la même car si nous ajoutons ou retirons un octet, la longueur sera toujours la même, seul le padding changera. L'attaquant ne voit que les données chiffrées et il n'a aucun moyen de connaître le padding à partir des données chiffrées.
Solution : jouer avec les spécifications du mode CBC, afin que la longueur du padding soit de 1 en ajoutant une valeur aléatoire dans la variable GARB (dans le paramètre GET, puisque l'attaquant contrôle les données GET et POST)
| bloc1 | bloc2 | bloc3 | bloc4 | longueur |
|---|---|---|---|---|
| GET /GARBc | ookie= DATA coo | kie=quokkalight + PAD(1) | 48 | |
| GET /GARBc | ookie=a DATA coo | kie=quokkalight | PAD(16) | 64 |
| GET /GARBc | ookie=b DATA coo | kie=quokkalight | PAD(16) | 64 |
| GET /GARBc | ookie=. DATA coo | kie=quokkalight | PAD(16) | 64 |
| GET /GARBc | ookie=q DATA coo | kie=quokkalight + PAD(1) | 48 |
Si l'octet correspond à un motif, la longueur sera la même ; s'il ne correspond pas, la longueur sera différente.
Ensuite, il suffit d'utiliser la même méthode expliquée dans la partie RC4, nous ajoutons simplement une étape avant.
adjust_padding() afin d'obtenir un padding de longueur 1two_tries_recursive() et nous pouvons trouver le FLAG secret !python3 CRIME-cbc-poc.py
arrive très bientôt...
\ /
\ _ /
----/_\----
x--------------( . )--------------x
x|x | |_|\_/|_| | x|x
x x x x