
🔪 CRIME-Angriff PoC : ein Kompressions-Orakel greift CVE-2012-4929 an 🔪
CRIME-Angriff: ein Kompressionsorakel-Angriff CVE-2012-4929, entdeckt von Juliano Rizzo und Thai Duong;
Bei einem Kompressionsorakel-Angriff kann die Verwendung adaptiver Datenkompression auf einer Mischung aus gewähltem Klartext und unbekanntem Klartext zu inhaltsabhängigen Änderungen der Länge des komprimierten Textes führen, die erkannt werden können, obwohl der Inhalt des komprimierten Textes selbst anschließend verschlüsselt wird. Dies kann bei Protokollangriffen genutzt werden, um zu erkennen, wenn der injizierte bekannte Klartext dem unbekannten Inhalt eines geheimen Teils der Nachricht auch nur teilweise ähnelt, was die Komplexität der Suche nach einer Übereinstimmung für den geheimen Text erheblich reduziert. Die CRIME- und BREACH-Angriffe sind Beispiele für Protokollangriffe, die dieses Phänomen nutzen.
Der CRIME-Angriff ermöglicht es, verschlüsselte Daten, die ein Client an einen Server sendet, mithilfe der Länge der verschlüsselten Daten abzurufen. Er ermöglicht es nicht, den privaten Schlüssel abzurufen, der zum Verschlüsseln der Nachricht oder der HTTP-Anfrage verwendet wird.
Viele Artikel erklären, wie der CRIME-Angriff funktioniert, aber das sind die besten Erklärungen, die ich im Internet gefunden habe:
Dieser Angriff ist wirklich nicht komplex, der wirklich interessante Teil ist die Implementierung, die sich ein wenig von der ‚Theorie‘ unterscheidet.
Schauen wir uns die naive Methode an, wie sie in dem Artikel beschrieben wird – sie ist ein guter Weg, um zu verstehen, wie es funktioniert:
Der Angreifer kann die vom Client gesendeten Anfragen kontrollieren (z. B. mit JavaScript). Das Ziel ist es, das geheime Cookie abzurufen. Der Angreifer sendet mehrere solcher Anfragen und prüft die Länge der verschlüsselten Daten:
Da cookie=q mit cookie=quokkalight aus dem geheimen Cookie übereinstimmt, ist die Länge der verschlüsselten Daten gleich, und der Angreifer weiß, dass er ein Byte gefunden hat.
Aber diese Methode schlug manchmal fehl und ist nicht zuverlässig, also verwenden wir stattdessen eine andere Methode.
Zuerst senden wir eine Anfrage mit dem Zeichen, das wir finden wollen, gefolgt von mehreren Zeichen, die in der ursprünglichen Anfrage nicht vorkommen können, etwa einigen Sonderzeichen: chr(i) + "#:/[@/&". Dann senden wir eine zweite Anfrage, aber wir invertieren die Nutzlast wie folgt: "#:/[@/&" + chr(i), und wir vergleichen die beiden Längen. Wenn len(enc(req1)) < len(enc(req2)), dann haben wir ein Byte gefunden. Diese Methode heißt two_tries und ist viel zuverlässiger:
Der Angreifer hat ein Byte gefunden!
if len(enc(request1)) < len(enc(request2)):
print("found byte")
Die von mir implementierte Methode two_tries ist vollständig rekursiv – aber warum? Manchmal kann mehr als ein Byte gefunden werden, weil die Kompression mit mehreren Mustern übereinstimmt.
Nehmen wir das Geheimnis: cookie=quokkalight. Wenn wir den Algorithmus two_tries ausführen, erhalten wir das folgende Ergebnis:
result 1: cookie=quokie=quokie=quokie=quokie=quokie=
result 2: cookie=quokkalight
Der Algorithmus muss alle Pfade im Baum durchlaufen, um alle möglichen Lösungen zu finden. Wir können alle Ergebnisse als einen Baum betrachten, der wie folgt dargestellt werden kann:

Der Proof of Concept des CRIME-Angriffs gegen den Stream-Chiffre-Modus befindet sich in der Datei: CRIME-RC4-poc.py. Dies ist eine Python-Implementierung der vorherigen Erklärung.
python3 CRIME-RC4-poc.py
Vollständige Demo und Ergebnis:
Wenn der CBC-Chiffriermodus mit AES oder DES verwendet wird, ist der Angriff nicht so einfach wie bei RC4. Da alles in Blöcke aufgeteilt ist, ist der Angriff ein wenig kniffliger (aber nicht allzu sehr).
Nehmen wir zum Beispiel an, wir verwenden AES mit dem CBC-Chiffriermodus. Der Block wird in eine Länge von 16 aufgeteilt, und am Ende wird ein Padding hinzugefügt, wenn len(data)%16 != 0.
Im CBC-Modus ist es wichtig zu beachten, dass die Nutzlast payload=rand eine Länge von 16 ergibt und nicht 12, da am Ende der Daten vor der Verschlüsselung ein Padding hinzugefügt wird. Unser vorheriger Angriff kann also nicht funktionieren.
Beispiel:
In diesem Beispiel ist die Länge immer gleich, denn wenn wir ein Byte hinzufügen oder entfernen, bleibt die Länge immer gleich, nur das Padding ändert sich. Der Angreifer sieht nur die verschlüsselten Daten und hat keine Möglichkeit, das Padding anhand der verschlüsselten Daten zu ermitteln.
Lösung: Spiele mit den Spezifikationen des CBC-Modus, sodass die Padding-Länge 1 beträgt, indem ein zufälliger Wert in die GARB-Variable eingefügt wird (im GET-Parameter, da der Angreifer die GET- und POST-Daten kontrolliert).
Wenn das Byte mit einem Muster übereinstimmt, ist die Länge gleich; wenn es nicht übereinstimmt, ist die Länge unterschiedlich.
Als Nächstes müssen wir nur dieselbe Methode verwenden, die im RC4-Teil erklärt wurde; wir fügen lediglich einen Schritt davor ein.
adjust_padding() auf, damit wir ein Padding der Länge 1 erhaltentwo_tries_recursive() auf und können das geheime FLAG finden!python3 CRIME-cbc-poc.py
kommt wirklich bald ...
\ /
\ _ /
----/_\----
x--------------( . )--------------x
x|x | |_|\_/|_| | x|x
x x x x
https://www.nccgroup.trust/globalassets/our-research/us/whitepapers/ssl_attacks_survey.pdf https://github.com/cloudflare/cf-nocompress https://www.ekoparty.org/archive/2012/CRIME_ekoparty2012.pdf https://security.stackexchange.com/questions/19911/crime-how-to-beat-the-beast-successor/19914#19914
| Anfrage | Länge |
|---|
| 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 |
| Anfrage zum Abrufen von Byte q | Länge |
|---|
| 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 |
| block1 | block2 | block3 | Länge |
|---|
| 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 |
| block1 | block2 | block3 | block4 | Länge |
|---|
| 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 |