Главная / Блог / Атаки на сессии в веб-задачах CTF: CSRF, session fixation и предсказуемые токены

14 мин.00

Атаки на сессии в веб-задачах CTF: CSRF, session fixation и предсказуемые токены

Атаки на сессии в веб-задачах CTF: CSRF, session fixation и предсказуемые токены

Атаки на сессии в веб-задачах CTF: CSRF, session fixation и предсказуемые токены

Веб-задача на 500 очков, два часа без подвижек — вся команда копала SQLi и XSS, а уязвимость оказалась в том, что session ID генерировался из MD5-хеша текущего timestamp. Один запрос к серверу для синхронизации часов, десять строк на Python — и cookie администратора угадан за полторы минуты. Обидно? Ещё как. Но именно так выглядит типичный промах на CTF: все бросаются на инъекции, а сессии остаются без внимания.

По данным Verizon DBIR 2025, 26% всех подтверждённых нарушений безопасности связаны с веб-атаками, и заметная доля из них — следствие ошибок в управлении сессиями. В CTF эта пропорция отражается напрямую: задачи на CSRF, session fixation и предсказуемые токены есть на каждом крупном соревновании, но процент решений по ним стабильно ниже, чем по классическим инъекциям. Разберём все три вектора в writeup-формате — с HTTP-запросами, кодом и конкретными приёмами для Burp Suite.

Управление сессиями в веб-CTF: где искать уязвимости

Каждое веб-приложение в CTF-задаче использует сессии для отслеживания аутентифицированного пользователя. После ввода логина и пароля сервер генерирует идентификатор сессии (session ID), записывает его в cookie через заголовок Set-Cookie и отправляет браузеру. Все последующие запросы автоматически включают эту cookie — сервер по ней определяет, кто перед ним. Подробнее — в нашем подробном разборе пентест веб-приложений.

Уязвимости управления сессиями классифицированы в OWASP Top 10 как A07:2021 — Identification and Authentication Failures. Сюда входят слабые пароли, credential stuffing, session fixation и ошибки жизненного цикла сессий. В MITRE ATT&CK атаки на сессии покрывают сразу несколько техник разных тактик:

  • Steal Web Session Cookie (T1539, Credential Access) — кража cookie через XSS, перехват трафика или вредоносное ПО
  • Web Session Cookie (T1550.004, Lateral Movement) — использование украденного cookie для перемещения между сервисами
  • Browser Session Hijacking (T1185, Collection) — перехват активной браузерной сессии
  • Web Cookies (T1606.001, Credential Access) — подделка cookie для получения доступа

В CTF-задачах инфраструктурный перехват трафика обычно не нужен. Основное — логические ошибки: сервер не обновляет session ID после логина, токен генерируется предсказуемо, или отсутствует защита от межсайтовой подделки запроса.

Типичная точка входа — посмотреть, как приложение выставляет cookie. Открываете Burp Suite Proxy, перехватываете ответ сервера на запрос страницы логина, смотрите на заголовок Set-Cookie. Три вопроса: меняется ли session ID после аутентификации? Есть ли флаги HttpOnly, Secure, SameSite? Насколько длинный и случайный токен? Ответы определяют, какой из трёх векторов применим.

Требования к окружению для практики

Для воспроизведения техник из статьи потребуется:

  • Burp Suite Community Edition (бесплатно) — для перехвата HTTP-запросов, анализа cookie и ручного тестирования. Для анализа энтропии токенов через Sequencer нужна Pro-версия (~449 USD/год)
  • Python 3.8+ с библиотекой requests (pip install requests) — для скриптов перебора токенов
  • Целевое приложение — CTF-задача или локальный стенд. Для самостоятельной практики подойдёт DVWA или WebGoat через Docker (docker pull vulnerables/web-dvwa)
  • ОС: Kali Linux 2024+ или любая ОС с Burp Suite и Python. RAM: от 4 ГБ (8 ГБ при запуске Docker-стенда параллельно с Burp)

CSRF атака в CTF: обход токенов и SameSite защиты

Механика межсайтовой подделки запроса

CSRF (Cross-Site Request Forgery) — атака, при которой вредоносная страница заставляет браузер жертвы отправить запрос к доверенному приложению от имени аутентифицированного пользователя. Согласно OWASP CSRF Prevention Cheat Sheet, атака работает потому, что браузер автоматически прикладывает cookie к каждому запросу на соответствующий домен. Сервер не способен отличить легитимный запрос от подделки — для него оба выглядят одинаково.

[Применимо: CTF web challenges с cookie-based аутентификацией, бот-администратор посещает предоставленную ссылку]

В CTF-задачах CSRF появляется в типичном сценарии: вам дают возможность отправить ссылку боту-администратору, который переходит по ней в браузере с активной сессией. Если на вашей странице размещена форма, автоматически отправляющая запрос к уязвимому приложению — действие выполняется с правами администратора. Красота.

Обход CSRF-токенов в CTF-задачах

Защита от CSRF строится на нескольких механизмах, и в CTF каждый из них можно встретить в ослабленном виде.

Synchronizer Token Pattern. Сервер генерирует CSRF-токен, вставляет его в форму через <input type="hidden" name="csrf_token">, при отправке проверяет совпадение с серверным значением. Типичные ошибки в CTF: токен проверяется, но не привязан к конкретной сессии (один валидный токен подходит для любого пользователя); проверка работает только для POST, а действие доступно через GET; токен передаётся в URL и утекает через заголовок Referer.

Первое, что стоит сделать — удалить параметр csrf_token из запроса в Burp Repeater и отправить повторно. Сервер принял запрос без токена? CSRF-защиты фактически нет. Я видел это на CTF чаще, чем хотелось бы.

SameSite cookie. Атрибут определяет, когда cookie отправляется вместе с кросс-доменными запросами. Strict блокирует cookie при любом кросс-доменном запросе. Lax разрешает при навигационных GET-запросах (клик по ссылке), но блокирует POST из чужого домена. None отправляет cookie всегда, но требует флага Secure. Современные браузеры (Chrome 80+, Firefox 86+) по умолчанию ставят SameSite=Lax, что блокирует CSRF через POST-формы. Но в CTF-задачах часто встречается явно выставленный SameSite=None или бот на устаревшем движке без дефолтного Lax.

Проверка Origin и Referer. Сервер может проверять, откуда пришёл запрос. Обход: заголовок Referer подавляется через <meta name="referrer" content="no-referrer"> в HTML-странице эксплойта. Если серверная логика построена как «если Referer присутствует — проверить, если нет — пропустить», атака проходит. Такая логика — подарок для атакующего.

Практический разбор: CSRF для смены пароля администратора

Сценарий: веб-приложение с формой смены пароля, cookie имеет SameSite=None; Secure (флаг HttpOnly может присутствовать, но на CSRF не влияет — браузер прикладывает cookie автоматически), CSRF-токен отсутствует. Бот-администратор переходит по ссылке, которую вы ему отправите. Эксплойт — HTML-страница на вашем сервере:

<html>
<body>
  <form action="https://target.ctf/change_password" method="POST">
    <input type="hidden" name="new_password" value="pwned_by_csrf">
    <input type="hidden" name="confirm" value="pwned_by_csrf">
  </form>
  <script>document.forms[0].submit();</script>
</body>
</html>

Бот загружает страницу, JavaScript мгновенно отправляет форму. Браузер прикладывает cookie бота к POST-запросу — сервер воспринимает это как легитимную смену пароля. Заходите с новым паролем и забираете флаг.

Ограничение техники. Если у cookie стоит SameSite=Lax (дефолт Chrome/Firefox), POST из кросс-доменного контекста не будет содержать cookie. Варианты обхода: найти state-changing endpoint, доступный через GET (/change_password?new=pwned), или обнаружить XSS на том же домене для выполнения запроса из same-origin контекста. В реальном пентесте (не CTF) ситуация аналогична: SameSite=Lax закрывает большинство CSRF-векторов, но не спасает при наличии XSS на том же домене — XSS-скрипт выполняется в origin приложения и обходит все кросс-доменные ограничения.

Session fixation: фиксируем сессию до аутентификации

Механика уязвимости session fixation

Session fixation (CWE-384, в контексте OWASP — часть A07:2021 Identification and Authentication Failures) — атака, при которой злоумышленник заставляет жертву аутентифицироваться с заранее известным session ID. В отличие от session hijacking, где крадут существующий токен, при fixation атакующий подсаживает свой токен до того, как жертва войдёт в систему. Разница принципиальная: не нужно ничего перехватывать, достаточно подсунуть.

[Применимо: CTF web challenges с PHP-бэкендом без фреймворковой обёртки, legacy-приложения без session regeneration]

Атака работает в три шага. Первый: атакующий получает валидный session ID от сервера (обычный GET-запрос, ответ содержит Set-Cookie). Второй: атакующий передаёт этот session ID жертве — через URL-параметр, cookie injection или скрытое поле формы. Третий: жертва вводит свои учётные данные, аутентифицируется — session ID не меняется, и атакующий уже знает валидный токен аутентифицированной сессии.

По данным Invicti, уязвимость возникает, когда приложение принимает session ID из внешних источников (GET-параметры, POST-данные) и не вызывает регенерацию идентификатора после успешного логина.

Пошаговая эксплуатация session fixation

В Burp Suite проверка занимает 30 секунд: перехватите запрос логина через Proxy, сравните значение PHPSESSID (или аналогичной cookie) в запросе до аутентификации и в ответе после. Значение не изменилось — перед вами session fixation. Всё, можно эксплуатировать.

Типичный CTF-сценарий с PHP-бэкендом: приложение принимает PHPSESSID через GET-параметр и не обновляет его после логина.

Шаг 1. Получаем session ID. Отправляем GET-запрос на https://target.ctf/ и извлекаем Set-Cookie: PHPSESSID=abc123def456 из ответа.

Шаг 2. Формируем ссылку для жертвы. Если приложение принимает session ID через URL: https://target.ctf/login?PHPSESSID=abc123def456. Если только через cookie — нужен дополнительный вектор: XSS для document.cookie="PHPSESSID=abc123def456" или CRLF injection для подстановки заголовка Set-Cookie.

Шаг 3. Бот (жертва) переходит по ссылке, видит форму логина, вводит свои учётные данные. Сервер аутентифицирует пользователя, но session ID остаётся abc123def456.

Шаг 4. Отправляем запрос с cookie PHPSESSID=abc123def456 к защищённой странице — сервер распознаёт аутентифицированную сессию и возвращает содержимое аккаунта жертвы, включая флаг.

Корректная защита — вызов session_regenerate_id(true) в PHP после аутентификации. Параметр true уничтожает старую сессию на сервере, делая известный атакующему session ID бесполезным.

Ограничения: когда session fixation не сработает

Фреймворк регенерирует session ID автоматически. Django, Flask-Login, Spring Security, Express с express-session — все они создают новый session ID при аутентификации по умолчанию. В CTF fixation чаще встречается на custom PHP или Node.js без фреймворковой обёртки. Увидели Flask — скорее всего, fixation не ваш вектор.

Приложение не принимает session ID извне. Если PHPSESSID выставляется только серверной стороной через cookie и не принимается через URL, прямая подмена через ссылку невозможна. Для fixation потребуется дополнительная уязвимость — XSS или CRLF injection на том же домене для вставки Set-Cookie.

Привязка сессии к IP или User-Agent. Некоторые приложения дополнительно проверяют, что IP-адрес или User-Agent совпадают с теми, что были при создании сессии. В CTF это редкость, но в задачах на 400-500 очков встречается. Обход: если бот крутится на том же хосте, что и приложение, IP может совпасть.

Timeout сессии. Если серверная сессия живёт 60 секунд, окно для эксплуатации минимальное — бот должен перейти по ссылке и аутентифицироваться до истечения срока жизни вашего подсаженного session ID. Тут уже вопрос скорости.

Предсказуемые токены сессий: брутфорс и анализ энтропии

Что делает токен предсказуемым

Энтропия токенов сессий — мера их случайности. Согласно рекомендациям OWASP, CSRF-токены (и session ID) должны быть уникальными, секретными и непредсказуемыми — сгенерированными криптографически стойким генератором (CSPRNG). В CTF-задачах часто встречаются токены, построенные на предсказуемых источниках:

Timestamp. MD5 или SHA1 от Unix time. Атакующий синхронизирует часы с сервером через HTTP-заголовок Date и перебирает хеши в окне ±30 секунд. 61 вариант для MD5 — доли секунды на перебор. Это тот самый случай из начала статьи.

Последовательный счётчик. Session ID вида session_1, session_2 или числовой инкремент. Перебор тривиален: зная свой ID, подставляете ID минус один, минус два и так далее. Встречается реже, чем хотелось бы авторам задач, но всё ещё попадается.

Слабый PRNG. Стандартные rand() в PHP или Math.random() в JavaScript — не криптографически стойкие. Зная несколько выходных значений, внутреннее состояние генератора восстанавливается, и все последующие токены предсказуемы.

Закодированные данные. Base64 от username:timestamp или JSON с предсказуемыми полями. Декодирование через echo "dXNlcjoxNjg4..." | base64 -d мгновенно раскрывает структуру и позволяет подделать токен для другого пользователя.

[Применимо: CTF web challenges с custom session management, legacy PHP без использования session_start()]

Анализ через Burp Suite Sequencer

Burp Suite Pro содержит модуль Sequencer для автоматической оценки случайности токенов. В Burp Proxy перехватите ответ с Set-Cookie, правой кнопкой отправьте в Sequencer, запустите Live Capture. Burp сам отправит сотни запросов и соберёт токены. После 200+ образцов — Analyze Now.

Sequencer показывает effective entropy в битах. Ниже 64 бит — токен уязвим к перебору при наличии вычислительных ресурсов. Ниже 32 бит — предсказуем при минимальных затратах. Для сравнения: нормальный session ID из session_start() в PHP или secrets.token_hex(32) в Python выдаёт 128+ бит энтропии.

В Community Edition Sequencer недоступен, но анализ можно провести вручную: собрать 50 токенов через for i in $(seq 1 50); do curl -s -I https://target.ctf/ | grep Set-Cookie; done, выгрузить значения в файл и визуально проверить на паттерны — повторяющиеся префиксы, последовательные блоки, корреляцию с временем запроса. Глаз цепляет паттерны быстрее, чем кажется.

Скрипт для перебора time-based токенов

Допустим, при анализе обнаружено, что session ID — MD5 от Unix timestamp. HTTP-заголовок Date в ответе сервера дал серверное время. Скрипт перебирает окно ±30 секунд:

import hashlib, requests, time

server_time = 1719500000  # Unix time из заголовка Date
url = "https://target.ctf/admin"

for offset in range(-30, 31):
    token = hashlib.md5(str(server_time + offset).encode()).hexdigest()
    r = requests.get(url, cookies={"session": token}, allow_redirects=False)
    if r.status_code == 200 and "Welcome" in r.text:  # не 302 redirect на /login
        print(f"[+] Token: {token} (offset={offset})")
        print(r.text)
        break
    time.sleep(0.1)

61 итерация выполняется за секунды. На практике окно может быть шире (±300 секунд), но MD5 считается мгновенно — даже 600 итераций не создают заметной нагрузки.

Ограничение техники. Если к timestamp добавлена соль (salt), перебор невозможен без знания этой соли. Проверка: запросите два токена в одну секунду. Если они различаются — используется CSPRNG или salt. Прямой перебор тут не поможет, и нужно искать утечку секрета через LFI, SSRF, раскрытие конфигурации (/debug, .env, /server-info) или ошибки приложения, выводящие stack trace с внутренними переменными.

Место атак на сессии в цепочке kill chain

Атаки на сессии в CTF-задачах редко существуют в вакууме — обычно это звено в цепочке из двух-трёх уязвимостей. Понимание позиции каждой техники в kill chain помогает определить, что искать до и после.

CSRF в терминах ATT&CK ближе всего к T1204.001 (Malicious Link) как способу доставки, но сама техника подделки запроса в матрице ATT&CK Enterprise явно не покрыта — это web-app-специфичный вектор, релевантный больше для OWASP-классификации (A01:2021 Broken Access Control). Жертва кликает ссылку, запускающую нежелательное действие. Предшествующий шаг — разведка: поиск state-changing endpoint без CSRF-защиты через анализ HTML-форм и заголовков в Burp. Результат — обычно смена пароля или привилегий, что открывает доступ к следующему этапу (админ-панель, внутренний API).

Session fixation работает на стыке Initial Access и Credential Access (T1078, Valid Accounts): атакующий получает аутентифицированный session ID без знания пароля. Предшествующий шаг — обнаружение того, что session ID не регенерируется. Результат — прямой доступ к аккаунту жертвы.

Предсказуемые токены — Credential Access (T1606.001, Forge Web Credentials: Web Cookies): атакующий вычисляет или подделывает session ID другого пользователя. Предшествующий шаг — анализ энтропии. Результат — подстановка угаданной cookie и доступ к целевому аккаунту, обычно администраторскому.

В задачах на 300+ очков сессионная уязвимость почти всегда комбинируется: через CSRF получаете привилегии администратора, через админку находите SSRF или file upload, и только оттуда — RCE и флаг. Decision tree для быстрого выбора вектора:

Наблюдение в Burp Вероятный вектор Следующий шаг
Session ID одинаковый до и после логина Session fixation Подготовить ссылку с фиксированным SID для бота
Формы без hidden csrf_token, cookie SameSite=None CSRF Создать HTML-эксплойт с автоотправкой формы
Короткие токены, паттерн или корреляция с Date Предсказуемые токены Анализ энтропии, скрипт перебора
HttpOnly отсутствует на session cookie XSS → cookie theft Искать отражённый/сохранённый XSS
Ничего из перечисленного Другой класс уязвимости SQLi, SSTI, path traversal

Чеклист: как распознать уязвимое управление сессиями в CTF

При анализе веб-задачи на предмет сессионных уязвимостей пройдите эти проверки в первые 10 минут:

  1. Session ID до и после логина. Перехватите cookie до аутентификации и после. Значение не изменилось — session fixation.
  2. Атрибуты cookie. Проверьте Set-Cookie в Burp: нет HttpOnly — путь для XSS-кражи; SameSite=None — CSRF через POST возможен; нет Secure — перехват на HTTP.
  3. Длина и формат токена. Менее 32 hex-символов — подозрительно. Base64-строка, которая декодируется в читаемый текст — потенциально предсказуема.
  4. CSRF-защита форм. Просмотрите HTML state-changing форм (смена пароля, настроек). Нет скрытого поля с токеном — удалите csrf_token из запроса в Burp Repeater и отправьте повторно.
  5. Заголовок Date vs session ID. Если токен похож на хеш — сравните с MD5/SHA1 от timestamp из серверного Date. Совпадение — предсказуемый токен.
  6. Приём session ID из URL. Добавьте ?PHPSESSID=test123 к запросу. Сервер принял — fixation через URL возможна.
  7. Множественные образцы. Соберите 10-20 токенов подряд. Инкремент, общий префикс, корреляция с временем — слабая энтропия.

Этот чеклист покрывает три основных вектора. Пройти его быстрее, чем два часа искать SQLi, которой в задаче может не быть.

Большинство CTF-игроков тратят первые часы на injection-атаки и обращают внимание на сессии, только когда всё остальное не сработало. Порядок стоит перевернуть: проверка сессий занимает минуты и закрывает целый класс задач, который выпадает из стандартного чеклиста. Если взглянуть на статистику категории Web на крупных CTF-площадках, задачи на session fixation и предсказуемые токены присутствуют регулярно, но решаются реже, чем SQLi или XSS той же сложности. Команды, которые умеют быстро опознать слабый PRNG или отсутствие session regeneration, получают очки там, где конкуренты буксуют.

Ещё одна вещь, которую мало кто проговаривает: изолированное знание CSRF без понимания того, как встроить его в цепочку — это половина навыка. В задачах выше 300 очков CSRF почти никогда не даёт флаг напрямую. Обычно через CSRF получаете привилегии, через привилегии добираетесь до SSRF или file upload, и только оттуда — до RCE. Сессионная атака — связующее звено, через которое одна уязвимость усиливает другую. То же касается реальных пентестов: cookie theft через XSS (T1539) + использование украденной сессии для доступа к внутреннему API (T1550.004) — стандартная двухступенчатая цепочка, которая встречается в каждом втором веб-проекте. Если хочешь не просто writeup, а пройти всю атаку самому — на WAPT эту цепочку проходят в течение двух модулей с лабами.

🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».

Поделиться

0 комментариев

Пожалуйста, войдите, чтобы оставить комментарий.

Загрузка комментариев...