Analyse des npm-Supply-Chain-Angriffs
Die Analyse der kürzlich aufgetretenen npm Supply Chain Attack zeigt, was die Angreifer erreichen wollten und wieso diese Angriffe verheerend sein...
Warum NTLMv1 unsicher ist und zwei der häufigsten Angriffsvektoren: Brute-Force-Angriffe (insbesondere Rainbow-Table-Angriffe) und Relaying-Angriffe.
Im vergangenen Jahr ist uns eine bestimmte Konfiguration in den Active Directory (AD)-Domänen unserer Kunden häufig begegnet: die Verwendung von NTLMv1. NTLMv1 ist eine veraltete Version des NTLM-Authentifizierungsprotokolls. Das NTLM-Protokoll selbst gilt als unsicher, und sogar Microsoft empfiehlt den Wechsel zu Kerberos, dem empfohlenen Standard für AD-Domänen, wie hier erwähnt.
NTLM and NTLMv2 authentication is vulnerable to various malicious attacks, including SMB replay, man-in-the-middle attacks, and brute force attacks. Reducing and eliminating NTLM authentication from your environment forces the Windows operating system to use more secure protocols, such as the Kerberos version 5 protocol, or different authentication mechanisms, such as smart cards.
In diesem Blogbeitrag werden wir uns damit befassen, warum das NTLMv1-Protokoll unsicher ist, und werden zwei der häufigsten Angriffsvektoren gegen das Protokoll untersuchen: Brute-Force-Angriffe und Relaying-Angriffe. Darüber hinaus werden wir uns eine bestimmte Art von Brute-Force-Angriffen genauer anschauen – Rainbow-Table-Angriffe.
Das NTLMv1-Authentifizierungsprotokoll ist ein relativ einfaches Challenge-Response-Protokoll. Der Client sendet zunächst eine NEGOTIATE_MESSAGE an den Server, um anzuzeigen, dass er sich authentifizieren möchte. Der Server sendet einen Challenge-Wert an den Client, der als CHALLENGE_MESSAGE bezeichnet wird. Der Client verwendet den NTLM-Hash seines (oder eines vom Benutzer angegebenen) Passworts als Verschlüsselungsschlüssel, um den Challenge-Wert zu verschlüsseln, und sendet den verschlüsselten Wert zurück an den Server. Dies ist die RESPONSE. Der Server leitet dann die RESPONSE und die CHALLENGE_MESSAGE an einen Domänencontroller weiter, der die Gültigkeit der Antwort überprüft. Wenn die Überprüfung erfolgreich ist, gilt der Authentifizierungsprozess als erfolgreich.

NTLMv1 verwendet den DES-Algorithmus (Digital Encryption Standard). Die RESPONSE-Nachricht besteht tatsächlich aus drei verketteten DES-Chiffretexten. Der Grund dafür ist der Längenunterschied zwischen NTLM-Hashes und DES-Schlüsseln. DES verwendet 7-Byte-Schlüssel, während NTLM-Hashes 16 Byte gross sind. Der NTLM-Hash wird daher mit fünf 0-Bytes erweitert, was zu einer Gesamtlänge von 21 Bytes führt, die dann in drei 7-Byte-Schlüssel aufgeteilt wird. Diese Schlüssel werden dann zur Verschlüsselung der Server-Challenge verwendet, was zu drei DES-Chiffretexten führt, die miteinander verkettet werden, um die Antwortnachricht zu bilden.
K1 | K2 | K3 = NTLM-Hash | 5-Bytes-0
RESPONSE = DES(K1,C) | DES(K2,C) | DES(K3,C)
DES ist ein sehr alter Algorithmus, der nach modernen Standards als unsicher gilt. Der Grund dafür ist der kleine 7-Byte-Schlüssel. Dies führt zu einem sehr kleinen Schlüsselraum von 256, wodurch der Algorithmus anfällig für Brute-Force-Angriffe ist. Die Situation wird noch dadurch verschärft, dass die drei Chiffretexte in der RESPONSE-Nachricht völlig unabhängig voneinander sind, was bedeutet, dass sie alle unabhängig voneinander geknackt werden können. Der dritte Chiffretext ist besonders gravierend, da er aus zwei zufälligen Bytes gefolgt von fünf 0-Bytes besteht, was effektiv zu einem Schlüsselraum von 65 536 oder 216 führt. Daher ist das Knacken des dritten Chiffretextes selbst mit begrenzten Rechenressourcen trivial.
Das Knacken der ersten beiden Chiffretexte ist jedoch schwieriger. Mit moderner Hardware ist es möglich, den gesamten Schlüsselraum von 256 direkt mit Brute-Force-Angriffen zu knacken. Unserer Erfahrung nach kann dies durch die Anmietung von High-End-GPUs erreicht werden, z. B. über Webseiten wie trooper.ai. Mit geeigneter Hardware (wir haben sechs NVIDIA RTX 4090 GPUs eingesetzt) kann ein NTLMv1-Hash innerhalb eines Tages geknackt werden, wobei die finanziellen Kosten bei etwa 50€ liegen, was es selbst für opportunistische Angreifer erschwinglich macht.
Es gibt jedoch eine noch schnellere Methode, um NTLMv1-Hashes zu knacken: Rainbow-Tabellen. Das Problem beim Knacken eines Hashes ist das Problem der Berechnung der Urbildfunktion einer gegebenen Ausgabe einer Einwegfunktion (im Wesentlichen eine Funktion ohne leicht berechenbare Umkehrfunktion). Es gibt zwei naive Ansätze zur Lösung dieses Problems: die Berechnung aller möglichen Eingabe-Ausgabe-Paare in Echtzeit, bis das Urbild gefunden ist, und die Speicherung aller möglichen Eingabe-Ausgabe-Paare in einer Lookup-Tabelle. Die erste Methode erfordert keinen zusätzlichen Speicherplatz, kann jedoch sehr lange dauern, während die zweite Methode sofortige Lookup-Zeiten bietet, jedoch grosse Mengen an Speicherplatz erfordert.
Rainbow-Tabellen sind Datenstrukturen, die versuchen, den Time-Memory Trade-Off (TMTO) zwischen diesen beiden Methoden zu optimieren. Selbst im Fall von DES mit einem kleinen Schlüsselraum kann das Ausprobieren aller möglichen Eingaben ohne Zugang zu modernster Hardware unzumutbar lange dauern. Darüber hinaus ist die Speicherung einer Lookup-Tabelle für alle möglichen DES-Eingabe-Ausgabe-Paare noch unzumutbarer, da dafür etwa 150 Petabyte Speicherplatz erforderlich wären.
Das Knacken von Hashes mit Rainbow-Tabellen erfolgt in zwei Phasen: der Offline-Phase und der Online-Phase. Während der Offline-Phase wird die Rainbow-Tabelle erstellt, während die Online-Phase die eigentliche Suche nach dem Urbild darstellt. Rainbow-Tabellen bieten eine Alternative zu den naiven Methoden, indem sie nicht Eingabe-Ausgabe-Paare, sondern Start- und Endpunkte von Ketten speichern. Ketten werden berechnet, indem die Hash-Funktion, die wir knacken wollen, zusammen mit einer Reduktionsfunktion wiederholt angewendet wird. Die Reduktionsfunktion bildet Hashes uniform auf die Menge der möglichen Eingaben zurück. Die Idee einer Reduktionsfunktion besteht im Wesentlichen darin, den Eingaberaum effektiv neu aufzubauen, um so viel wie möglich vom eigentlichen Eingaberaum abzudecken. So wird effektiv eine Umkehrfunktion simuliert. In der Praxis wird eine Familie von Reduktionsfunktionen verwendet, um Kettenkollisionen zu reduzieren, d. h. wenn zwei Ketten einen Zwischenpunkt gemeinsam haben, erzeugen beide Ketten von diesem Punkt an denselben Satz von Punkten. Eine typische Reduktionsfunktion ist ri (x) = (x + i) mod N, für i ∈ { 1, ... , t -1 } wobei N die Grösse des Eingaberaums ist. Die Struktur einer Rainbow-Tabelle ist unten dargestellt, wobei t die Kettenlänge und m die Anzahl der Start- und Endpunktpaare ist. Dabei ist zu beachten, dass nur die Startpunkte Si und Endpunkte Ei gespeichert werden.

Bei der Erstellung von Rainbow-Tabellen ist die Wahl der Werte für t und m sehr wichtig. Diese beiden Werte stellen den Kompromiss zwischen Zeit und Speicherplatz dar. Je länger die in der Rainbow-Tabelle gespeicherten Ketten sind (d. h. je höher der Wert von t ist), desto weniger Start- und Endpunktpaare müssen gespeichert werden, da mehr Hashes durch die Zwischenpunkte der Ketten abgedeckt werden. Dies würde jedoch zu einer langsameren Online-Phase führen, d. h. zu längeren Suchzeiten. Das Gegenteil trifft ebenfalls zu, d. h. je mehr Ketten gespeichert sind (je höher der Wert von m ist), desto kürzer können die Ketten sein. Der Kompromiss ist in diesem Fall natürlich ein grösserer Speicherplatzbedarf. Gängige Werte für t sind 10 000 und 100 000. Für saubere Rainbow-Tabellen maximaler Grösse ergibt sich die erwartete Anzahl von Ketten für die Länge t aus mtmax ≈ 2N/(t+2) , wobei N die Grösse des Eingaberaums ist. Eine saubere Rainbow-Tabelle enthält nur eindeutige Endpunkte. Eine Rainbow-Tabelle ist von maximaler Grösse, wenn die Wahrscheinlichkeit, einen neuen eindeutigen Endpunkt zur Tabelle hinzuzufügen, vernachlässigbar ist. Somit können wir die Anzahl der Ketten für t = 10 000 und t = 100 000 für unser DES-Cracking-Problem berechnen. Für t = 100 000 gilt:

Dies entspricht etwa 2,8 Terabyte Speicherplatz, was selbst für Heim-PCs eine verfügbare Menge sein kann. Analog erhalten wir für t = 10 000 etwa 28 Terabyte Speicherplatz, was zwar mehr Investitionen erfordert, aber dennoch machbar ist.
Die Rechenkosten für die Erstellung einer solchen Rainbow-Tabelle würden jedoch einen höheren Aufwand erfordern. Experimente mit den neuesten Techniken zur Erstellung von Rainbow-Tabellen für einen Eingaberaum von 242 dauerten mit einem 128-Kern-Prozessor etwa acht Stunden. Für einen Eingaberaum von 256 wäre die Laufzeit unzumutbar lang (die Laufzeit erhöht sich linear mit der Grösse des Eingaberaumes). Um Rainbow-Tabellen maximaler Grösse zu erstellen, müssten mehrere moderne GPUs eingesetzt werden. Alternativ könnte man einen kleineren Eingaberaum verwenden, z. B. durch Verwendung einer grossen Wortliste. Dies würde die Erfolgswahrscheinlichkeit beim Auffinden des richtigen Urbildes verringern, aber die Vorberechnungsphase kostengünstiger machen.
Während der Online-Phase erfolgt die Suche nach dem Urbild x eines Hashwerts y = h(x) spaltenweise, beginnend mit der letzten Spalte. Die Suche beginnt mit der Suche nach einem Endpunkt Ei , sodass Ei = rt-1(y) , d. h. wir möchten überprüfen, ob die Anwendung von h auf den Zwischenpunkt Xi,t-1 zu dem Hash y führen würde. Wird ein solcher Punkt gefunden, wird die Kette neu berechnet, um zu sehen, ob h(Xi,t-1) = y ist. Wenn ja, wird die Suche beendet mit x = Xi,t-1 . Ist dies nicht der Fall, handelt es sich um einen Fehlalarm. Bei einem Fehlalarm oder wenn kein passender Endpunkt gefunden wird, wird die Suche in der nächsten Spalte der Rainbow-Tabelle fortgesetzt, d. h. rt-1(h(rt-2(y)) wird berechnet, und der oben beschriebene Vorgang wird für diesen Wert wiederholt. In diesem Fall überprüfen wir effektiv, ob h(Xi,t-2) = y ist. Die Suche wird dann spaltenweise fortgesetzt, bis entweder eine Übereinstimmung gefunden oder die Tabelle erschöpft wird.
Der Grund, warum dieser Prozess funktioniert, liegt darin, dass die Reduktionsfunktionen den Eingaberaum auf eine uniform zufällige Weise konstruieren. Daher wird eine sehr hohe Anzahl von Hashes durch die Ketten in der Rainbow-Tabelle „abgedeckt“. Beim Knacken von Passwort-Hashes müssen wir technisch gesehen nicht das genaue Passwort finden, sondern eine Eingabe, die zu demselben Hash führt. Aus diesem Grund können wir ziemlich sicher sein, dass der Hash, den wir knacken wollen, in einer der Ketten der Rainbow-Tabelle zu finden ist. In der Praxis werden mehrere Rainbow-Tabellen mit unterschiedlichen Reduktionsfunktionsfamilien verwendet, um die Wahrscheinlichkeit von Kettenkollisionen weiter zu verringern. Die Erfolgswahrscheinlichkeit für l saubere Rainbow-Tabellen maximaler Grösse beträgt 1 - e-2l . Für l = 4 ergibt dies eine Wahrscheinlichkeit von etwa 99,97%.
Bei Rainbow-Table-Angriffen auf NTLMv1 müssen die Tabellen für einen bestimmten Challenge-Wert im Voraus berechnet werden. Das bedeutet, dass der Angreifer bei der Imitation eines Servers immer dieselbe Challenge senden muss, damit die Rainbow-Tabellen überhaupt nutzbar sind. Ein beliebter Challenge-Wert ist 1122334455667788, da es Dienste gibt, die Rainbow-Tabellen für diesen Challenge-Wert schon berechnet haben. Die Verwendung dieses Challenge-Werts ist jedoch auffällig, da moderne EDRs so konfiguriert sind, dass sie bei dessen Erkennung Warnmeldungen generieren. Deshalb sollte ein anderer Wert bei der Vorberechnung der Rainbow-Tabelle benutzt werden, um einen Rainbow-Table-Angriff auf eine OPSEC-sichere Weise durchzuführen.
Rainbow-Table-Angriffe können durch das Salten der Eingaben der Hash-Funktion wirksam mitigiert werden. Da Rainbow-Tabellen im Voraus berechnet werden müssen, müsste ein Angreifer jeden möglichen Salt-Wert berücksichtigen und daher für jeden möglichen Salt-Wert separate Rainbow-Tabellen berechnen. Die Einführung eines 16-Byte-Salts würde beispielsweise erfordern, dass der Angreifer 2128 Rainbow-Tabellen speichert. Aus der obigen Analyse geht hervor, dass selbst für unseren relativ bescheidenen Fall weltweit nicht genügend Speicherkapazität vorhanden ist, um solche Anforderungen zu erfüllen. Wir können jedoch feststellen, dass der einzige Zufallswert im gesamten NTLMv1-Protokoll, d.h. die Challenge, effektiv vom Angreifer kontrolliert wird. Dies ist ein Grund, warum im neueren NTLMv2-Protokoll ein Client-Challenge-Wert eingeführt wurde, um einen weiteren Zufallswert zu haben, der ausserhalb der Kontrolle des Angreifers liegt und im Zusammenhang mit Brute-Force-Angriffen als Salt-Wert angesehen werden kann.
Ein weiterer Nachteil des NTLMv1-Protokolls besteht darin, dass die während des Authentifizierungsprozesses ausgetauschten Daten in keiner Weise an eine bestimmte Session gebunden sind. Mit anderen Worten: Der Client kann nicht überprüfen, ob die Challenge tatsächlich von dem Server stammt, bei dem er sich authentifiziert, während der Server nicht überprüfen kann, ob eine Antwort tatsächlich an ihn adressiert war. Das bedeutet, dass diese Daten, wenn sie abgefangen werden, vom Angreifer an ein beliebiges Ziel weitergeleitet werden können. Solche Angriffe werden als Relay-Angriffe bezeichnet.
Ein Relay-Angriff beginnt damit, dass der Angreifer entweder eine Authentifizierungsnachricht abfängt oder ein Ziel dazu zwingt, sich bei einer vom Angreifer kontrollierten Maschine zu authentifizieren. In beiden Fällen wird die Authentifizierung an einen beliebigen Dienst weitergeleitet, um diesen dazu zu bringen, eine bestimmte böswillige Aktion auszuführen. Häufige Relay-Ziele für solche Angriffe sind Active Directory Certificate Services (AD CS), SMB und LDAP. Ein Beispiel für einen Relay-Angriff ist unten zu sehen.

Eine Möglichkeit, Relay-Angriffe zu mitigieren, besteht darin, Session Signing zu aktivieren, d.h. SMB-Signing, LDAP-Signing, usw. Dadurch werden Nachrichten effektiv an ihre Absender gebunden, sodass sowohl das Ziel eines Relay-Angriffs als auch der Dienst, an den weitergeleitet wird, die Authentizität der empfangenen Nachrichten überprüfen können. Eine gute Übersicht mit Techniken, Sicherheitsmassnahmen und Auswirkungen von Relay-Angriffen wurde auf The Hacker Recipes veröffentlicht.

Wenn Session Signing aktiviert ist, kann der Angreifer die Session nicht klauen, da er den für die Signierung erforderlichen Sessionsschlüssel nicht abrufen kann. Ein Relay ist jedoch weiterhin möglich, wenn der Angreifer an ein Protokoll weiterleitet, bei dem die Session-Integrität nicht über NTLM-Nachrichten kontrolliert wird, z.B. HTTPS oder LDAPS. Alternativ kann der Angreifer die NEGOTIATE_MESSAGE ändern und das Flag NTLMSSP_NEGOTIATE_SIGN deaktivieren. Um solche Angriffe zu verhindern, wurde ein Message Integrity Code (MIC) implementiert, bei dem es sich um einen HMAC_MD5 handelt, der auf die Verkettung aller drei NTLM-Nachrichten unter Verwendung des Sessionsschlüssels angewendet wird. Damit soll sichergestellt werden, dass die NTLM-Nachrichten mit dem MIC nicht manipuliert wurden. Diese Sicherheitsmassnahme kann jedoch auch mit einem Angriff umgangen werden, der als „Drop the MIC“ bekannt ist. Wie der Name schon sagt, wird bei diesem Angriff das MIC zusammen mit dem Versionsfeld aus der RESPONSE-Nachricht entfernt. Zusätzlich müssen mehrere Flags in der NEGOTIATE_MESSAGE (NTLMSSP_NEGOTIATE_ALWAYS_SIGN und NTLMSSP_NEGOTIATE_SIGN) und RESPONSE (NTLMSSP_NEGOTIATE_ALWAYS_SIGN, NTLMSSP_NEGOTIATE_SIGN, NEGOTIATE_KEY_EXCHANGE und NEGOTIATE_VERSION) deaktiviert werden. „Drop the MIC“ wurde in einem Patch von Microsoft behoben.
Die obige Analyse verdeutlicht nicht nur die Schwächen des NTLM-Protokolls, sondern auch ein häufiges Problem bei Versuchen, ein unsicheres Kommunikationsprotokoll zu patchen. Häufig führen Massnahme zur Bekämpfung eines Angriffs zu neuen Angriffsvektoren. In diesem Fall haben wir gezeigt, wie die verschiedenen von Microsoft implementierten Sicherheitsmassnahmen mit Downgrade-Angriffen umgangen werden können. Diese Umgehungen sind nur in Domänen mit laxen Signatur-Durchsetzungsregeln möglich. Solche Konfigurationen sind aufgrund von Abwärtskompatibilitätsüberlegungen üblich, die oft auf Kosten der Sicherheit gehen. Es wird daher immer empfohlen, das Signieren zu erzwingen. Mit dieser Massnahme wird eine strenge Signatur-Durchsetzungsrichtlinie implementiert, wodurch Nachrichten ohne MIC und Sessionsignatur von dem Dienst, der Ziel des Relay-Angriffs ist, abgelehnt werden.
In diesem Blogbeitrag haben wir zwei der beliebtesten Angriffsvektoren gegen das NTLMv1-Protokoll genauer angeschaut. Unsere Untersuchungen haben gezeigt, dass beide Angriffsvektoren auch von opportunistischen Angreifern ohne umfangreiche rechnerische und organisatorische Ressourcen ausgenutzt werden könnten. Dies zeigt, dass NTLMv1 ein sehr veraltetes Protokoll mit mehreren Schwachstellen ist, das so schnell wie möglich durch die verbesserten Kerberos- oder NTLMv2-Protokolle ersetzt werden sollte.
Die Analyse der kürzlich aufgetretenen npm Supply Chain Attack zeigt, was die Angreifer erreichen wollten und wieso diese Angriffe verheerend sein...
Ein Blogbeitrag über mögliche Angriffe auf KeePass in der Standardkonfiguration und Härtungsmassnahmen.