Восемь часов, 24 команды, пять уязвимых сервисов - и чекер, который на каждом втором тике возвращал
CORRUPT из-за race condition в собственном коде проверки. Три человека дебажили Python-скрипт прямо во время соревнований, пока команды в чате писали, что скорборд сломан. Когда организуешь Attack-Defense CTF, именно такие моменты определяют, получится рабочее соревнование или восьмичасовой хаос с багами собственной инфраструктуры.Дальше - разбор того, что должно работать под капотом: компоненты gameserver, vulnbox, написание чекеров, сетевая изоляция и автоматизация SLA-проверок. Пригодится и тем, кто организует A/D CTF, и тем, кто собирается участвовать впервые и хочет понимать, как всё устроено изнутри.
CTF соревнования архитектура: три компонента Attack-Defense
Attack-Defense CTF строится на трёх обязательных компонентах: gameserver, vulnbox команд и game network. Ломается любой из них - рассыпается всё.Gameserver - центральный узел, управляющий ходом игры. Его задачи:
- размещать флаги на vulnbox каждой команды в начале каждого тика;
- запускать чекеры для проверки работоспособности сервисов (SLA);
- принимать захваченные флаги от команд через submission-сервер;
- считать очки и рисовать скорборд.
Vulnbox - сервер каждой команды с набором уязвимых сервисов. Все команды получают идентичные конфигурации. Vulnbox должен быть доступен из game network всё соревнование - gameserver хранит на нём флаги и через него определяет, жив ли сервис.
Game network - сеть, связывающая vulnbox всех команд и gameserver. Через неё команды атакуют друг друга, а gameserver выполняет SLA-проверки.
CTF gameserver настройка: тики, скоринг, модульная архитектура
Игра делится на тики (раунды). Один тик - от 1 до 5 минут, зависит от организатора. За тик gameserver размещает новые флаги, проверяет SLA, а команды атакуют и защищаются. Типичный A/D CTF: 8 часов с тиками по 2-3 минуты - порядка 160-240 раундов.Если копнуть глубже: по данным проекта CTF Gameserver (FAUST CTF, открытый исходный код, активно поддерживается и используется ежегодно), он состоит из независимых модулей, которые общаются через общую PostgreSQL-базу:
- Controller - координирует прогресс соревнования: текущий тик, генерация флагов.
- Checker Master - запускает checker-скрипты для каждого сервиса каждой команды в каждом тике. Основная нагрузка ложится именно сюда.
- Checkerlib - библиотека для написания checker-скриптов. Поддерживаются Python и Go.
- Submission - принимает захваченные флаги.
- Web - Django-приложение для регистрации, скорборда и информационных страниц.
По данным maplebacon primer, скоринг складывается из трёх компонентов:
| Тип очков | Источник | Вес |
|---|---|---|
| Attack Points | Успешная эксплуатация сервиса другой команды + сдача флага | Зависит от CTF |
| Defense Points | Никто не украл флаг с вашего сервиса за тик | Зависит от CTF |
| SLA Points | Сервис работает корректно и проходит проверки gameserver | Обычно значительный |
Пропорции различаются от CTF к CTF, но SLA-баллы - то, что команды теряют чаще всего. И теряют по собственной вине: криво патчат сервис, чекер перестаёт проходить - и привет, минус очки каждый тик.
Сетевая инфраструктура CTF: VPN, NAT и изоляция команд
Сетевая топология в Attack-Defense CTF следует стандартному паттерну. По документации Enowars и FAUST CTF, типовая адресация:- Game Network:
10.0.0.0/16(или10.60.0.0/16) - Vulnbox команды N:
10.0.0.N/32(или10.60.N.1) - Game Router:
10.0.1.1 - Flag Submission:
10.0.13.37:1337
- WireGuard - быстрее, проще в настройке, меньше накладных расходов. Требует ядро Linux 5.6+ на стороне сервера. CTF Gameserver (FAUST) упоминает поддержку WireGuard для VPN Status.
- OpenVPN - универсальнее, работает на любой ОС, но тяжелее. Enowars исторически предоставляет OpenVPN-конфиг для подключения.
10.0.1.1, независимо от реального источника. Зачем? Команды не должны различать трафик чекера и трафик атакующих. Без NAT можно тупо заблокировать все пакеты от чужих IP и пропускать только gameserver - и вся защита превращается в одно правило iptables.VPN-подсеть каждой команды (например,
10.0.240.0/24) полностью изолирована от подсетей других команд - участники одной команды не видят VPN-сегмент другой.С точки зрения MITRE ATT&CK, в контексте A/D CTF: Network Sniffing (T1040, Discovery / Credential Access) - штатная тактика. Перехват трафика в game network позволяет видеть эксплойты других команд. Network Service Discovery (T1046, Discovery) - стандартная практика на старте: команды определяют открытые порты сервисов на чужих vulnbox.
Требования к окружению для развёртывания
Минимальный стек для организации Attack-Defense CTF на 20-30 команд:| Компонент | Ресурсы | Примечание |
|---|---|---|
| Gameserver (controller + checker master + web) | 8 vCPU, 16 GB RAM, SSD 100 GB | Чекеры - основная нагрузка: при 5 сервисах и 30 командах - 150 проверок за тик |
| PostgreSQL | 4 vCPU, 8 GB RAM | Общая БД для всех модулей gameserver |
| VPN-сервер (WireGuard/OpenVPN) | 2 vCPU, 4 GB RAM | Маршрутизация + NAT, отдельный от gameserver |
| Vulnbox каждой команды | 2-4 vCPU, 4-8 GB RAM | Docker Compose с 3-5 сервисами |
ОС: Debian 12 или Ubuntu 22.04 LTS - стандарт для CTF Gameserver. Канал: от 1 Гбит/с на стороне gameserver. Режим: облачный (Hetzner, AWS, Selectel) предпочтительнее - проще масштабировать. Для деплоя vulnbox 30 команд хорошо работает Ansible: один playbook поднимает идентичные VM с Docker Compose-конфигом. Мониторинг состояния раундов - Prometheus + Grafana с метриками checker master (процент OK/DOWN/FAULTY по сервисам, latency чекеров).
CTF vulnbox настройка: сервисы, Docker Compose и первый час
Сервис в Attack-Defense CTF - аналог задачи в Jeopardy, но с двумя отличиями: его нужно не только ломать, но и защищать, и он должен оставаться работоспособным для чекера. По сути это Exploit Public-Facing Application (T1190, Initial Access) - то, что команды делают с сервисами друг друга.Типичный набор для A/D CTF:
- 1-2 web-сервиса (Python/Flask, Go, Node.js) - SQL injection, SSTI, IDOR
- 1-2 pwn-сервиса - бинарные приложения с buffer overflow, format string
- 1 crypto/misc - ошибки в криптографии или бизнес-логике
Развёртывание сервисов в подавляющем большинстве случаев - через
docker compose up -d. Некоторые CTF (ENOWARS) исторически предоставляют готовый образ VM, другие (FAUST CTF) требуют самостоятельного развёртывания из VM-образа. Реже используются LXC-контейнеры.Первый час после получения доступа к vulnbox:
- Подключиться к vulnbox (VPN-конфиг или SSH-креды).
- Скопировать все сервисы на локальную машину - бэкап до любых изменений. Без этого откат сломанного патча невозможен.
- Поднять сервисы (
docker compose up -d), проверить статус на скорборде. - Открыть папку каждого сервиса в IDE и начать анализ: искать уязвимости, планировать патчи.
Написание CTF чекеров: checkerlib, SLA и типичные ошибки
Чекер - скрипт, который притворяется обычным пользователем сервиса. Каждый тик Checker Master запускает чекер для каждого сервиса каждой команды. Чекер делает две вещи:- PUT - размещает флаг в сервисе: регистрирует пользователя, отправляет сообщение, загружает файл - зависит от логики конкретного сервиса.
- GET - проверяет, что ранее размещённый флаг доступен: логинится по сохранённому токену, запрашивает данные.
OK. Если нет - команда теряет SLA-баллы.Написание чекеров - одна из самых муторных задач при организации CTF платформы. Чекер должен быть достаточно умным, чтобы проверить реальную функциональность, и достаточно устойчивым, чтобы не падать от сетевых таймаутов. Структура на Python с использованием checkerlib (FAUST CTF Gameserver):
Python:
from checkerlib import BaseChecker, BrokenServiceException
class ServiceChecker(BaseChecker):
def place_flag(self, tick):
flag = self.get_flag(tick)
token = self._register_user(flag)
self.store_state(f"token_{tick}", token)
def check_flag(self, tick):
token = self.load_state(f"token_{tick}")
if self.get_flag(tick) not in self._get_data(token):
raise BrokenServiceException("Flag missing")
place_flag и check_flag вызываются в разных тиках - состояние сохраняется через store_state / load_state. Каждый вызов должен иметь таймаут (10-30 секунд). И чекер обязан рандомизировать имена пользователей, user-agent, порядок полей - иначе команды отфильтруют его трафик по паттерну. Если чекер всегда создаёт checker_user_42 - его вычислят за первый тик.Состояния SLA: что означает каждый статус
По документации FAUST CTF Gameserver, сервис в каждом тике может находиться в одном из состояний:| Статус | Значение | Что делать |
|---|---|---|
| OK | Сервис работает, флаги на месте | Штатная ситуация |
| DOWN | Нет ответа, таймаут, разрыв соединения | Проверить Docker-контейнеры, логи, сеть |
| FAULTY | Сервис отвечает, но ведёт себя не по спецификации | Откатить последний патч |
| FLAG_NOT_FOUND | Сервис работает, но конкретный флаг не найден | Проверить, не удалились ли данные |
| RECOVERING | Текущий флаг OK, но флаги прошлых тиков недоступны | Проверить хранилище |
Самая частая причина
FAULTY - кривой патчинг. Команда закрывает SQL injection через фильтрацию ключевых слов, а заодно режет легитимные запросы чекера. Или отключает эндпоинт, через который чекер сохраняет флаг. Правило простое: патч должен блокировать эксплуатацию уязвимости, не ломая штатную функциональность. На практике это сложнее, чем звучит.Ограничения техник и типичные ошибки
Для организаторов:- Race conditions в чекере. Если controller запускает
place_flagиcheck_flagдля одного тика параллельно (баг или кривая конфигурация), чекер проверяет флаг до его размещения. Лечится нагрузочным тестированием до начала CTF: разверните 5-10 тестовых vulnbox и прогоните 50 тиков. Не поленитесь - на самом CTF будет поздно. - Недостаточная рандомизация. Если чекер всегда создаёт пользователя с предсказуемым паттерном, команды его вайтлистят. Генерируйте случайные данные в каждом вызове.
- Хрупкость к сетевым задержкам. Чекер без обработки
ConnectionTimeoutмассово выдаётDOWNпри любом всплеске латентности в game network. Оборачивайте каждый сетевой вызов в try/except с адекватными таймаутами.
- Патч, блокирующий ключевое слово
SELECT, может заблокировать и легитимные запросы чекера, если сервис хранит данные в SQL. Классический выстрел в ногу. - Закрытие порта файрволом вместо патчинга кода гарантирует
DOWN- чекер не достучится до сервиса. - Удаление endpoint-а из маршрутов web-приложения ломает
place_flag- статус переходит вFAULTY.
Автоматизация проверки SLA CTF: фермы флагов и анализ трафика
Ручная эксплуатация в A/D CTF - путь к поражению. Флаги обновляются каждые 1-3 минуты, команд 20-30. Проэксплуатировать каждую вручную, забрать флаг, отправить на submission - физически невозможно. Здесь работает Automated Collection (T1119, Collection): автоматизированный сбор флагов со всех команд в каждом тике.Ферма флагов - фреймворк, который запускает ваш эксплойт-скрипт против всех команд параллельно, парсит флаги из stdout и отправляет их на submission-сервер. Два основных инструмента:
- S4DFarm (команда C4T BuT S4D, активно поддерживается) - веб-интерфейс, статистика по каждому эксплойту, поддержка нескольких протоколов submission (ForcAD, RuCTF, VolgaCTF). Для командной работы - самое то.
- DestructiveFarm (DestructiveVoice) - проще в настройке, подходит для первого A/D или одиночной игры. Конфигурация через
config.py.
Python:
#!/usr/bin/env python3
import sys, requests
host = sys.argv[1]
r = requests.get(f"http://{host}:8080/api/notes",
params={"id": "1 UNION SELECT flag FROM secrets"})
for line in r.text.split("\n"):
if len(line) == 32 and line[-1] == "=":
print(line, flush=True)
#!/usr/bin/env python3 (ферма запускает скрипт как процесс) и print(flag, flush=True) (ферма парсит stdout). Запуск через S4DFarm: python3 start_sploit.py --server-url http://FARM_IP/ --server-pass YOUR_PASS exploit.py.Attack-Defense флаги сервисы: сниффинг и защита трафика
Все команды в одной сети - трафик между ними доступен для перехвата. Network Sniffing (T1040) здесь не хак, а штатная тактика. Для новичков это ключевая стратегия: не можешь найти уязвимость сам - посмотри HTTP-запрос другой команды с рабочим эксплойтом и адаптируй.Tulip (OpenAttackDefenseTools) - инструмент для анализа PCAP в A/D CTF. Веб-интерфейс для фильтрации по сервисам, командам и паттернам. На vulnbox захват трафика запускается через
tcpdump -i eth0 -w /tmp/capture.pcap -s 0, но за 8 часов с 30 командами набирается 50+ ГБ - держите место на диске.Для защиты от известных эксплойтов применяется iptables:
Bash:
iptables -I FORWARD 1 -t filter -p tcp \
-d $VULNBOX_IP --dport $SERVICE_PORT \
-m string --string "UNION SELECT" --algo bm -j DROP
UNION SELECT на уровне ядра. Ограничения существенные: не работает с TLS-трафиком, не ловит обфусцированные запросы, а ошибка в паттерне (например, *) может заблокировать SSH и оставить вас без доступа к vulnbox. При ошибке - немедленно iptables-restore < /tmp/backup.txt. По данным A/D CTF Cheatsheet, более продвинутая альтернатива - reverse proxy перед каждым сервисом с regex-фильтрацией на вход и выход, но нужно проверять, что proxy не добавляет задержку, из-за которой чекер превысит таймаут.Распределение ролей в команде на Attack-Defense CTF
Для команды из 5-8 человек оптимальное распределение:| Роль | Задачи | Инструменты |
|---|---|---|
| Инфраструктурщик (1 чел.) | VPN, ферма, Tulip, мониторинг SLA | S4DFarm, Docker, tcpdump, Tulip |
| Патчер-дежурный (1 чел.) | Отслеживает скорборд, откатывает сломанные патчи | docker compose, git |
| Web-атака (1-2 чел.) | Анализ web-сервисов, эксплуатация | Burp Suite, Python, sqlmap |
| Pwn/Rev-атака (1-2 чел.) | Реверс бинарных сервисов, эксплойты | Ghidra, pwntools, GDB |
| Трафик-аналитик (1 чел.) | PCAP-мониторинг, извлечение чужих эксплойтов | Tulip, Wireshark |
Если команда впервые на A/D - три приоритета: (1) поднять и не ронять сервисы; (2) настроить ферму флагов; (3) захватить трафик и переиспользовать чужие эксплойты. Атака с нуля требует опыта, но адаптация увиденного в трафике - рабочая стратегия для первого раза. На HackerLab.pro можно попрактиковаться в отдельных категориях web и pwn, которые составляют основу сервисов на A/D CTF - это российская CTF-платформа экосистемы Codeby с задачами разного уровня сложности, нужна регистрация.
Большинство команд, впервые приезжающих на Attack-Defense, готовятся к нему как к Jeopardy на стероидах: прокачивают навыки поиска уязвимостей, тренируются на HTB, пишут быстрые эксплойты. А потом проигрывают командам, которые хуже ищут баги, но лучше автоматизируют инфраструктуру.
Неудобная правда: на A/D CTF побеждает не тот, кто первым нашёл RCE, а тот, кто первым настроил ферму и раскидал этот RCE на 25 команд за один тик. Я видел, как команда с тремя OSCP-сертификатами занимала последнее место, потому что не умела поднять S4DFarm и сдавала флаги руками через curl. И видел, как второкурсники входили в десятку, потому что в первый час развернули Tulip, настроили ферму и переиспользовали чужие эксплойты из трафика.
Для организатора картина зеркальная: можно написать пять блестящих сервисов с элегантными уязвимостями, но если чекеры не протестированы под нагрузкой, NAT не маскирует source IP, а gameserver не масштабируется - соревнование превращается в шоу багов собственной инфраструктуры. На тестирование чекеров и сетевого стека уходит в три раза больше времени, чем на разработку самих сервисов. Attack-Defense CTF - в первую очередь инженерная дисциплина, и только потом охота на баги. Если хочешь не просто writeup, а пройти всю атаку самому - WAPT с лабой на каждый кейс.
Последнее редактирование: