
На PicoCTF 2021 задача «Cookies» решалась перебором одного числового значения в cookie-заголовке — значение 18 из сотни возможных возвращало флаг picoCTF{3v3ry1_l0v3s_c00k135_bb3b3535}. Три минуты в Burp Intruder, задача закрыта. Звучит как халява, но именно на таких задачах нарабатывается чутьё, которое потом отличает человека, нашедшего IDOR в продакшене банка, от того, кто прошёл мимо. В веб-категории CTF большинство задач начального и среднего уровня завязаны на манипуляции с HTTP заголовками и cookies — подмену ролей, спуфинг IP-адреса, подделку сессионных токенов. Разберём каждый вектор от простого к сложному, с конкретными шагами в DevTools и Burp Suite.
HTTP заголовки — служебные пары «ключ: значение», которыми браузер и сервер обмениваются при каждом запросе. На странице их не видно, но именно в них лежит всё самое вкусное: кто запрашивает ресурс, с какого адреса пришёл, какую сессию использует, какой у него уровень доступа. В CTF разработчики задач намеренно прячут подсказки и уязвимости именно в заголовках — потому что новички обычно смотрят только на HTML-код страницы и дальше не копают.
Заголовки делятся на четыре группы: General Headers (общие), Request Headers (отправляются браузером), Response Headers (приходят от сервера) и Entity Headers (описывают тело сообщения). Для CTF критичны две — Request и Response. В Response Headers сервер может выдать подсказку: нестандартный заголовок с именем вроде X-Flag, X-Admin-Status или Header-RootMe-Admin. А в Request Headers вы отправляете то, что сервер будет проверять — и вот тут начинается самое интересное.
Первое действие на любой веб-задаче CTF — открыть DevTools (F12 в Chrome или Firefox), перейти на вкладку Network и обновить страницу. Кликните на первый запрос к целевому URL и посмотрите две секции: Response Headers и Request Headers.
На задаче «HTTP - Headers» платформы root-me.org страница показывает текст «Content is not the only part of an HTTP response!» — прямым текстом говорят: смотри заголовки ответа. В Response Headers обнаруживается нестандартный заголовок Header-RootMe-Admin: none. Сервер сообщает: ваш статус — не администратор. Логика решения: отправить в Request Headers тот же заголовок со значением true. Для этого подойдёт расширение ModHeader для Chrome или прямой запрос через curl -H "Header-RootMe-Admin: true" на URL задания.
Ключевой паттерн тут: сервер доверяет данным из Request Headers без валидации. Это прямое нарушение A01:2021 (Broken Access Control) по OWASP, где 94% протестированных приложений содержали ту или иную форму нарушения контроля доступа. В CTF такое встречается на каждом шагу. В продакшене — тоже, но об этом не принято говорить вслух.
Cookies — фрагменты данных, которые сервер записывает в браузер пользователя и получает обратно с каждым запросом. В CTF задачах cookies часто хранят роль пользователя, уровень доступа или идентификатор сессии в открытом виде. Если значение не подписано криптографически — его можно подменить и получить привилегии администратора. Просто так.
Требования к окружению. Для воспроизведения примеров нужны: браузер Firefox или Chrome (любая актуальная версия), доступ к DevTools (встроены в браузер), утилита curl (предустановлена в Linux и macOS, на Windows — через WSL или Git Bash). RAM: 2 ГБ хватит для работы с браузером. Интернет-соединение требуется для подключения к CTF-платформам (root-me.org, picoCTF.org).
Откройте DevTools (F12), перейдите на вкладку Application (в Chrome) или Storage (в Firefox). В левой панели разверните раздел Cookies и кликните на домен задания. Перед вами таблица с колонками: Name, Value, Domain, Path, Expires, Size и флаги (HttpOnly, Secure, SameSite).
На задаче root-me.org «HTTP - Cookies» после перехода на страницу со списком email-адресов сервер возвращает сообщение «You need to be admin». Заглядываем в исходный код страницы — а там закомментированная строка <!--SetCookie("ch7", "visiteur");-->. Разработчик забыл удалить отладочный комментарий (классика). Во вкладке Application видна cookie ch7 со значением visiteur. Двойной клик на значение, замена на admin, обновление страницы — и флаг ml-SYMPA на экране.
Альтернативный путь без вкладки Application: откройте консоль (вкладка Console в DevTools) и выполните document.cookie = 'ch7=admin'. Работает, только если cookie не имеет флага HttpOnly — о флагах поговорим ниже.
Третий способ — через curl, минуя браузер целиком:
curl -H "Cookie: ch7=admin" -v \
http://challenge01.root-me.org/web-serveur/ch7/?c=visiteur
Ключ -H добавляет произвольный заголовок к запросу, -v показывает полный обмен заголовками. Этот подход удобнее, когда нужно быстро проверить гипотезу, не переключаясь между вкладками браузера. Я обычно начинаю с curl — быстрее набрать команду в терминале, чем кликать по интерфейсу.
DevTools хороши для разведки и простых подмен, но для серьёзной работы с HTTP заголовками и cookies в CTF нужен перехватывающий прокси. Burp Suite Community Edition (бесплатная версия, portswigger.net) перехватывает каждый запрос между браузером и сервером и даёт редактировать любое поле до отправки.
Требования к окружению. Burp Suite Community Edition работает на Windows, Linux и macOS. Минимум 4 ГБ RAM (Burp запускается на JVM и съедает 500 МБ–1 ГБ). Для маршрутизации трафика через Burp настройте прокси в браузере: 127.0.0.1:8080. В Firefox: Settings → Network Settings → Manual Proxy Configuration. Для удобства переключения между обычным серфингом и перехватом поставьте расширение FoxyProxy — создайте профиль Burp и включайте его одним кликом. Обязательно установите CA-сертификат Burp для перехвата HTTPS: перейдите на http://burp/ при активном прокси, скачайте cacert.der, импортируйте в Firefox через Settings → Privacy & Security → Certificates → View Certificates → Authorities → Import.
Workflow при решении веб-задач CTF через Burp Suite укладывается в три фазы.
Фаза 1: пассивная разведка. Выключите Intercept (кнопка «Intercept is off»), походите по приложению и наблюдайте за Proxy → HTTP History. Тут видно все эндпоинты, параметры в URL и POST-телах, нестандартные заголовки вроде X-Role, X-Admin или Authorization, формат сессионных токенов (Base64, JWT, опак-строка). Отсортируйте по Status Code — ответы 302 и 403 часто указывают на скрытую функциональность.
Фаза 2: перехват и модификация. Включите Intercept, выполните действие в браузере (отправка формы, переход по ссылке). Запрос замирает в окне Burp — редактируйте заголовки, cookies, тело запроса. Нажмите Forward для отправки или Drop для отмены. Типичный сценарий в CTF: форма с полем role=user в POST-теле, защищённая JavaScript-валидацией на клиенте. Заполните форму нормально, включите Intercept перед нажатием Submit, поменяйте role=user на role=admin в перехваченном запросе, отправьте. Серверу плевать, что у вас было в JavaScript — он получает ваш вариант.
Фаза 3: итерация через Repeater. Правый клик на запрос в HTTP History → «Send to Repeater». В Repeater вы отправляете запрос повторно с любыми изменениями без участия браузера. Это основной инструмент для перебора значений cookies, тестирования заголовков и отладки exploit-цепочки. На задаче PicoCTF «Cookies» именно через Intruder (автоматизированный перебор) перебирались значения cookie name от 0 до 100 — значение 18 вернуло флаг, согласно разбору участника соревнований.
Заголовок X-Forwarded-For изначально предназначен для передачи реального IP-адреса клиента, когда запрос проходит через прокси или балансировщик нагрузки. В CTF-задачах серверная логика нередко проверяет этот заголовок для ограничения доступа: «страница доступна только с 127.0.0.1» или «только из внутренней сети 10.0.0.0/8».
Подмена тривиальна: добавьте X-Forwarded-For: 127.0.0.1 в Request Headers через Burp Intercept или через curl -H "X-Forwarded-For: 127.0.0.1". Сервер примет значение из заголовка вместо реального IP.
[Применимо: CTF и legacy-приложения без reverse proxy] В реальных средах за Nginx или Cloudflare заголовок X-Forwarded-For перезаписывается на уровне прокси, и клиентская подмена не работает. Но в CTF и на устаревших приложениях без правильной конфигурации обратного прокси — работает стабильно.
Аналогичный подход применяется к другим заголовкам запроса:
Referer — сервер проверяет, откуда пришёл пользователь. На задаче canyouhack.it (разбор с cybber.ru) требовалось, чтобы Referer содержал «google.com». Решение через curl -e "http://google.com" — ключ -e устанавливает Referer.
User-Agent — проверка типа клиента. Задачи формата «доступ только из браузера X» или «покажи контент для робота Googlebot» решаются подменой этого заголовка.
Host — по данным исследования PortSwigger, манипуляция заголовком Host открывает целый класс уязвимостей: от password reset poisoning до SSRF через маршрутизацию. В CTF встречаются задачи на виртуальный хостинг, где нужно подобрать правильное имя хоста для доступа к скрытому приложению.
С точки зрения MITRE ATT&CK подмена заголовков относится к технике Transmitted Data Manipulation (T1565.002, Impact) — атакующий модифицирует данные в транзите для изменения поведения приложения.
Когда сервер устанавливает cookie, он может задать флаги, ограничивающие доступ к ней. В CTF понимание флагов определяет выбор инструмента для атаки.
HttpOnly запрещает доступ к cookie из JavaScript. Если флаг установлен, document.cookie в консоли эту cookie не покажет, и XSS-атака не сможет её украсть. Но — внимание — в DevTools на вкладке Application cookie с HttpOnly-флагом видна и редактируема. Флаг защищает от скриптов на странице, не от инструментов разработчика. Через Burp Suite HttpOnly-cookie доступна в любом случае — прокси работает на сетевом уровне, а не в контексте браузера.
Secure означает, что cookie передаётся только по HTTPS. На HTTP-соединении браузер не включит эту cookie в запрос. Для CTF-задач на HTTP (без TLS) этот флаг обычно отсутствует, иначе задача была бы нерешаемой через перехват.
SameSite контролирует отправку cookie при межсайтовых запросах. Значения: Strict (только same-site), Lax (same-site + навигационные GET) и None (всегда, но требует Secure). Влияет на CSRF-атаки — с Strict или Lax браузер не отправит cookie при переходе с внешнего домена.
Практический вывод для CTF: если cookie имеет HttpOnly — забудьте про document.cookie, работайте через DevTools Application tab или Burp. Если HttpOnly нет — консоль браузера будет самым быстрым путём. Это напрямую связано с техникой Steal Web Session Cookie (T1539, Credential Access) по MITRE ATT&CK — понимание флагов определяет, каким методом атакующий может извлечь cookie.
Простейшие CTF-задачи хранят в cookie строку вроде admin=false или role=user в открытом виде. Задачи посложнее кодируют значение в Base64 или используют сериализованные объекты. Самые интересные — задачи с подписанными сессиями, где нужно подобрать секретный ключ.
Base64-куку распознать легко: строка из букв, цифр, символов +, / и = на конце. Декодируйте через echo "dXNlcj1hZG1pbg==" | base64 -d в терминале или через CyberChef в браузере — 30 секунд, и видно содержимое user=admin. Меняете значение, кодируете обратно через echo -n "role=admin" | base64, подставляете в cookie.
Подписанные токены — другая история. Flask по умолчанию использует подписанные сессии: данные закодированы в Base64 и подписаны HMAC с секретным ключом. Без знания ключа подпись не совпадёт, и сервер отвергнет cookie. На PicoCTF 2021 задача «Most Cookies» требовала именно этого: в исходном коде сервера был список возможных секретных ключей для Flask-сессии, и участникам нужно было перебрать их, чтобы подделать подпись cookie с нужным значением.
PHP-десериализация — ещё один частый вектор в CTF. На задаче «Super Serial» (PicoCTF 2021) cookie login обрабатывалась через unserialize(base64_decode(urldecode(...))). Атакующий создавал PHP-объект нужного класса, сериализовывал его, кодировал в Base64 и подставлял в cookie:
<?php
class access_log {
public $log_file;
}
$obj = new access_log;
$obj->log_file = "../flag";
echo base64_encode(serialize($obj));
// TzoxMDoiYWNjZXNzX2xvZyI6MTp7czo4OiJsb2dfZmlsZSI7czo3OiIuLi9mbGFnIjt9
?>
Результат подставлялся в cookie login, и сервер десериализовывал объект, читая файл с флагом. Классический пример A08:2021 (Software and Data Integrity Failures) по OWASP — небезопасная десериализация пользовательского ввода.
Ключевое отличие для CTF-игрока: если cookie выглядит как Base64 без точек — попробуйте декодировать и подменить напрямую. Если видите структуру с точками (три сегмента через точку) — скорее всего JWT или Flask-сессия, и нужен секретный ключ для подписи.
Манипуляция HTTP заголовками и cookies в CTF — не изолированный трюк, а часть полноценной цепочки атаки. В терминах kill chain эти техники покрывают несколько этапов.
Initial Access — эксплуатация веб-приложения через подмену заголовков (Exploit Public-Facing Application, T1190). Спуфинг X-Forwarded-For или Host для доступа к административным панелям или внутренним ресурсам.
Credential Access — кража (T1539) или подделка (T1606.001, Web Cookies) сессионных cookie. В CTF вы подделываете cookie локально, в реальной атаке — крадёте через XSS или перехватываете через Network Sniffing (T1040) на незащищённом HTTP-соединении.
Lateral Movement — использование украденной cookie для доступа к аккаунту другого пользователя (Web Session Cookie, T1550.004). В CTF это переход от пользователя visiteur к admin, в реальном сценарии — перемещение между аккаунтами внутри приложения.
Когда техники не работают. Подмена X-Forwarded-For бесполезна за корректно настроенным reverse proxy (Nginx с proxy_set_header X-Forwarded-For $remote_addr перезаписывает клиентское значение). Десериализация не сработает, если приложение использует json_decode вместо unserialize или верифицирует подпись cookie. Подмена cookies через document.cookie невозможна для HttpOnly-cookie. В реальных пентестах WAF (ModSecurity, Cloudflare WAF) блокируют аномальные значения заголовков, а современные фреймворки (Django 4.x, Rails 7+) подписывают сессии по умолчанию.
Понимание ограничений — то, что отделяет решателя CTF-задач от пентестера. В CTF уязвимость гарантирована условиями задачи. В реальном приложении первые 80% времени уходит на проверку предположений, которые не подтверждаются. И это нормально.
Тем, кто только начинает разбираться в веб-безопасности, полезно не просто решать задачи, а фиксировать для каждой: какой вектор сработал, какой заголовок или cookie был уязвим и почему. Формулировка «cookie ch7 хранит роль пользователя без подписи — подмена на admin даёт доступ к административному функционалу» — это уже строка из отчёта по пентесту. Именно такой навык ценится на собеседованиях: не «я решил 50 задач на root-me», а «я понимаю, почему сервер доверял клиентским данным и какой класс уязвимости это представляет».
На практике я вижу одну и ту же картину: новички, которые прошли десятки cookie-задач на CTF-платформах, на реальном пентесте теряются, потому что привыкли к гарантированному флагу. В жизни cookie может быть подписана, заголовок перезаписан прокси, а десериализация вообще не используется. Но навык смотреть в DevTools первым делом, перехватывать запросы в Burp и думать «а что если подменить это значение» — переносится один в один. Проблема не в технике, а в методологии: CTF учит находить конкретный баг, а пентест требует системного перебора гипотез. Если вы проходите веб-задачи и каждый раз записываете не только решение, но и три гипотезы, которые не сработали, — вы уже на полпути к рабочему пентесту. На курсе IB Basics эту цепочку от CTF-задачки до реального отчёта разбирают с лабами — для тех, кому мало просто щёлкать флаги без понимания, что за ними стоит.
🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».
0 комментариев
Пожалуйста, войдите, чтобы оставить комментарий.
Загрузка комментариев...