Продолжаем решать задачки по информационной безопасности web-серверов. Сегодня задачка с портала root-me.org, называется "JSON Web Token (JWT) - Introduction". За решение задачки дают 20 баллов, сложнее начального уровня.
Нам предлагают стать администратором, чтобы получить флаг. По названию задания и ресурсам можно догадаться, что работать придётся с JWT.
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/ch58/
Видим форму аутентификации. Есть возможность выполнить гостевой вход. Нажимаем Login as Guest!
Welcome guest to this website! :)
Видимо, мы выполнили аутентификацию под пользователем guest. Раз задачка на JWT, то этот токен должен где-то лежать.
Собственно, поиски JWT были недолгими. Есть Cookie jwt:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Imd1ZXN0In0.OnuZnYMdetcg7AWGV6WURn8CFSfas6AQej4V9M13nsk
Мы можем прочитать первую и вторую часть токена, header и payload. Для этого нам потребуется Base64 декодер. Я один такой написал:
Берём заголовок eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 и декодируем, поучается:
{"typ":"JWT","alg":"HS256"}
Из заголовка становится понятно, что для получения подписи используется алгоритм шифрования HS256.
Берём полезную нагрузку eyJ1c2VybmFtZSI6Imd1ZXN0In0 и декодируем, поучается:
{"username":"guest"}
Ага, в полезной нагрузке передаётся имя пользователя guest. Было бы неплохо передать там другое имя, например, admin. Зная секретный ключ можно было бы сформировать новый токен JWT с нужным нам именем пользователя, однако, нигде на сайте задания секретный ключ я не нашёл.
Но есть другой способ обойти проблему. Помимо подписанных токенов JWT бывают и неподписанные. Неподписанные токены не содержат третью часть и не используют шифрование. Мы можем проверить, использует ли сервер проверку на доступные алгоритмы шифрования. Для отключения шифрования вместо алгоритма нужно указать none.
Формируем JSON для заголовка без алгоритма шифрования:
{"typ":"JWT","alg":"none"}
Конвертируем заголовок в Base64:
eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0
Формируем JSON для полезной нагрузки с пользователем admin:
{"username":"admin"}
Конвертируем полезную нагрузку в Base64:
eyJ1c2VybmFtZSI6ImFkbWluIn0
Формируем неподписанный JWT:
eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJ1c2VybmFtZSI6ImFkbWluIn0
Если на сервере нет проверки на алгоритм шифрования, то он должен принять наш токен. Подменим гостевой токен на наш. Для изменения cookie использую расширение EditThisCookie.
Меняю токен JWT.
Обновляю страницу, и... Welcome admin to this website
А вот и флаг: S1gn4tuR3_v3r1f1c4t10N_1S_1MP0Rt4n7
Валидируем.
Флаг подходит, зарабатываем 20 очков.
Безопасность
- Если на сервере не стоит проверка на наличие подписи у токена, то злоумышленник может указывать собственные значения в полезной нагрузке. Проблема решается простым отбрасыванием неподписанных объектов. Что м с вами успешно и сделали. А ещё мы познакомились с технологией аутентификации с помощью токенов JWT.
- Злоумышленник может использовать JWT в атаках типа CSRF. Одним из методов борьбы с CSRF является добавление специальных заголовков с зашифрованной информацией, подтверждающей отправку запроса с доверенного сервера. Таким образом, если JWT используется не в качестве cookie, CSRF-атака становится невозможной.
- Если JWT хранится в DOM-хранилище, то система может быть подвержена XSS-атаке. При использовании cookie можно выставить HttpOnly-флаг, который предотвращает доступ JavaScript к хранилищу. Таким образом, злоумышленник не сможет извлечь токен и приложение становится защищенным от XSS.
Ещё один способ получить флаг, передав JWT в cookie с помощью curl:
curl -H "cookie: jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJ1c2VybmFtZSI6ImFkbWluIn0" \
-v http://challenge01.root-me.org/web-serveur/ch58/