Перейти к содержимому
← Каталог php Auth Bypass

JWT: Weak Secret

JWT подписан слабым секретом — подберите его через brute-force и подделайте токен администратора.

easyphpPro
Задача
# JWT: подбор слабого HMAC-секрета подписи ## Сценарий Вы — аудитор безопасности **SecureShop** (PHP + Apache + `firebase/php-jwt`). Аутентификация построена на JWT-токенах с алгоритмом HS256: после успешного входа сервер выдаёт клиенту HMAC-подписанный токен в cookie `token`, и каждый последующий запрос авторизуется по нему. В отличие от лаб про `alg: none` и Key Confusion, здесь сервер корректно настраивает алгоритм и проверяет подпись «как положено». Уязвимость спрятана в другом месте — в качестве самого секрета подписи. По данным внутреннего аудита, в админ-панели хранится конфиденциальная метка (CTF-флаг), доступная только пользователям с ролью `admin`. Пароль администратора атакующему неизвестен; ваша задача — получить admin-доступ и извлечь флаг, имея только обычную учётную запись `demo` / `demo`. ## Цель 1. Залогиньтесь как `demo` / `demo` и изучите структуру токена в cookie `token`: алгоритм подписи (HS256), claims (`user_id`, `username`, `role: user`, `exp`). 2. Найдите способ восстановить HMAC-секрет — атака полностью офлайн, без обращения к серверу. 3. Подпишите новый токен с восстановленным секретом и значением `role: admin`, подмените cookie и получите доступ к `/admin`. Извлеките CTF-флаг. ## Теория JWT-подпись HS256 (HMAC-SHA256) — **симметричная**: тот же секрет, что использовался для подписи, нужен и для проверки. Криптостойкость HMAC-SHA256 как алгоритма не имеет значения, если злоумышленник может перебрать сам секрет. По BCP RFC 8725, HMAC-секрет должен содержать минимум столько энтропии, сколько даёт сам алгоритм — для HS256 это **256 бит** (32 случайных байта). Если секрет короткий, словарный (`secret`, `password`, `key`, `jwt_secret`, `your_secret_key`) или иначе предсказуемый, любой, у кого на руках есть один валидный токен, может подобрать его офлайн — без обращения к серверу, без блокировок по rate-limit и без следов в логах. Готовые инструменты делают это за секунды: - `hashcat -m 16500` (mode для JWT HS*) — словарь rockyou.txt; - `ticarpi/jwt_tool.py -C -d <wordlist>` — комбинированный инструмент; - `john --format=HMAC-SHA256` — альтернатива hashcat. Усиливает уязвимость практика помещать `role`/привилегии напрямую в payload токена: тот, кто контролирует подпись, контролирует все авторизационные решения. В этой лабе именно так и происходит — функция получения текущего пользователя берёт `role` из claims, а не из БД. **Уязвимый PHP-паттерн:** ```php // VULNERABILITY: weak, dictionary-word secret — brute-forceable in milliseconds $GLOBALS['JWT_SECRET'] = 'secret'; // later, in getUser(): $decoded = JWT::decode($token, new Key($GLOBALS['JWT_SECRET'], 'HS256')); // ... // VULNERABILITY: role overridden from JWT claims $user['role'] = $decoded['role'] ?? $user['role']; ``` Подпись `JWT::decode` сама по себе корректна; проблема — в `'secret'`. После того как атакующий восстановил это значение по словарю, он может подписать любой токен — `firebase/php-jwt` его примет как валидный. ## Таблица атак на JWT | Класс атаки | Идея | Применима в этой лабе | |-------------|------|----------------------| | Brute-force HMAC-секрета | Подобрать слабый HS256-секрет по словарю (hashcat, jwt_tool) | **Да** — основной вектор | | Подмена `role` в claims | Поменять `role` на `admin` после подписания валидным секретом | **Да** — финальный шаг | | `alg: none` | Объявить отсутствие подписи | Нет — `firebase/php-jwt` отклоняет `alg: none` | | Key Confusion (RS256 → HS256) | Подменить алгоритм; использовать публичный ключ как HMAC-секрет | Нет — сервер изначально на HS256, асимметричной схемы и публичного ключа нет | | JWK header injection | Передать свой ключ в заголовке токена | Нет — секрет берётся из серверной переменной, не из токена | ## Точка входа атаки | Параметр | Значение | |----------|----------| | Обычная учётная запись | `demo` / `demo` | | Алгоритм JWT | HS256 (HMAC-SHA256) | | Эндпоинт логина | `POST /login` (выдаёт cookie `token` с HS256-JWT) | | Имя cookie | `token` | | Привилегированный эндпоинт | `GET /admin` (требует `role: admin` в подписанном токене) | | Где находится флаг | в HTML-ответе `/admin` в карточке Admin Flag, формат `flag{...}` |
🚧 Сайт в разработке. Полный функционал пока недоступен. Все вопросы — support@hackandfix.ru