Главная / Блог / CTF-задания 2025: от идеи до деплоя | Полный гайд
На прошлой неделе мой коллега Дима запустил свое первое CTF-задание для внутреннего хакатона. Казалось бы, простая SQL injection — что может пойти не так? Через час после старта половина команд получила флаг, просто подключившись к базе напрямую. Оказалось, что в Docker-контейнере был открыт порт MySQL, а пароль root'а — "password123". Вот так одна забытая строчка в docker-compose.yml превратила четырехчасовое задание в пятиминутку.
Эта история — классический пример того, почему создание качественных CTF-заданий требует системного подхода. Недостаточно просто придумать уязвимость и написать код. Нужно продумать архитектуру, безопасность, тестирование и мониторинг.
[Junior] Базовые требования:
[Middle] Рекомендуемый уровень:
[Senior] Для глубокого погружения:
Пристегнись, сейчас разберем, как устроено качественное CTF-задание изнутри.
CTF-задание (2025 стандарт)
├── Концептуальный уровень
│ ├── Философия и образовательная цель
│ ├── Выбор категории (веб/крипто/реверс/форензика/эксплуатация)
│ └── Определение сложности (easy/medium/hard)
├── Техническая реализация
│ ├── Docker-контейнер с изоляцией
│ ├── Динамические флаги (уникальные для каждой команды)
│ ├── Система валидации и проверки
│ └── Мониторинг и логирование
├── Деплой и инфраструктура
│ ├── CI/CD pipeline для автотестов
│ ├── Production environment (VPS/Cloud)
│ ├── Reverse proxy и SSL
│ └── Backup и восстановление
└── Пост-релиз поддержка
├── Авторский writeup с разбором
├── Мониторинг решений команд
├── Hotfix при критичных багах
└── Сбор фидбека для улучшений
Каждый уровень критично важен. Пропустишь концептуальную проработку — получишь задание-пустышку. Забудешь про мониторинг — узнаешь о проблемах от разъяренных участников.
Образовательная ценность превыше сложности
Современные CTF-задания должны обучать конкретным навыкам ИБ, а не проверять способность гуглить обскурные CVE. Хорошее задание — это мини-курс по конкретной уязвимости или технике.
[Middle] Принципы качественного задания:
[Senior] Продвинутые принципы:
Статические флаги — прошлый век. Команды делятся решениями, что убивает соревновательность. Динамические флаги генерируются индивидуально для каждой команды.
А теперь самое мясо — как это работает на практике:
ПРИМЕР: Генератор динамических флагов
Язык: Python 3.11+
Зависимости: hashlib, secrets, sqlite3
1. ИНИЦИАЛИЗАЦИЯ:
- создать базу данных flags.db с таблицами teams, challenges, team_flags
- загрузить секретный ключ MASTER_SECRET из переменных окружения
2. ГЕНЕРАЦИЯ ФЛАГА ДЛЯ КОМАНДЫ:
- получить team_id и challenge_id из запроса
- создать уникальную строку: f"{team_id}:{challenge_id}:{MASTER_SECRET}"
- вычислить SHA256 хеш от уникальной строки
- сформировать флаг: f"ugra_{хеш[:16]}_{случайный_суффикс}"
3. ВАЛИДАЦИЯ ФЛАГА:
- получить присланный флаг от команды
- пересчитать ожидаемый флаг для этой команды
- сравнить флаги, обновить статистику в БД
Сложность: O(1) по времени, O(n) по памяти (где n — количество команд)
Edge cases: дублирующиеся team_id, некорректные флаги, атаки на генератор
Реализация для CTFd:
CTFd поддерживает динамические флаги через плагины. Создаем кастомный challenge type:
# Фрагмент плагина для CTFd
class DynamicValueChallenge(BaseChallenge):
def generate_flag(self, team_id, challenge_id):
seed = f"{team_id}-{challenge_id}-{self.secret_key}"
flag_hash = hashlib.sha256(seed.encode()).hexdigest()[:16]
return f"ugra_{flag_hash}_{secrets.token_hex(4)}"
Проверено на практике — такой подход исключает списывание и делает каждое решение уникальным.
Тренды 2025:
[Junior] Базовые веб-уязвимости:
[Middle] Современные веб-атаки:
[Senior] Продвинутые сценарии:
Давай по порядку разберем создание веб-задания:
ПРИМЕР: Веб-задание с SQL injection
Язык: Python 3.11+ (Flask)
Зависимости: flask, sqlite3, werkzeug
1. СОЗДАНИЕ УЯЗВИМОГО ПРИЛОЖЕНИЯ:
- настроить Flask app с SQLite базой
- создать таблицу users с полями: id, username, password, role
- добавить админа с флагом в поле secret_data
2. УЯЗВИМАЯ ФУНКЦИЯ ЛОГИНА:
- получить username и password из POST запроса
- выполнить SQL запрос БЕЗ параметризации:
* query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
* результат = execute_query(query)
- если найден пользователь с role='admin' → показать флаг
3. ЗАЩИТА ОТ АВТОМАТИЗАЦИИ:
- добавить rate limiting: максимум 10 попыток в минуту
- логировать все попытки входа для мониторинга
- использовать CAPTCHA после 3 неудачных попыток
Уязвимость: классический SQL injection через ' OR '1'='1' --
Решение: username=admin' OR '1'='1' -- &password=anything
Популярные направления:
[Middle] Типичное крипто-задание:
ПРИМЕР: RSA с общим модулем
Язык: Python 3.11+
Зависимости: pycryptodome, gmpy2
1. ГЕНЕРАЦИЯ КЛЮЧЕЙ (уязвимо):
- создать два RSA ключа с ОДИНАКОВЫМ модулем n
- использовать разные экспоненты e1=3, e2=65537
- зашифровать флаг обоими ключами: c1, c2
2. АТАКА (для участников):
- найти НОД(e1, e2) = 1 (взаимно простые)
- использовать расширенный алгоритм Евклида
- найти коэффициенты a, b: a*e1 + b*e2 = 1
- вычислить m = (c1^a * c2^b) mod n
3. ВАЛИДАЦИЯ:
- проверить что полученное m содержит флаг
- декодировать из числа в строку
Сложность: O(log(min(e1,e2))) для алгоритма Евклида
Образовательная цель: понимание важности уникальных модулей в RSA
Тренды 2025:
Инструменты для российских команд:
Эволюция в 2025:
Специфика для российских CTF:
[Junior] Выбор темы:
[Middle] Проектирование архитектуры:
ПРИМЕР: Планирование многоэтапного задания
Язык: Концептуальный
Зависимости: техническое задание, mind mapping
1. ОПРЕДЕЛЕНИЕ ОБРАЗОВАТЕЛЬНЫХ ЦЕЛЕЙ:
- основная цель: изучение SQL injection
- дополнительные навыки: анализ исходного кода, bypass фильтров
- уровень сложности: medium (решают 30-50% команд)
2. РАЗБИВКА НА ЭТАПЫ:
- этап 1: найти точку входа (форма поиска)
- этап 2: обойти базовую фильтрацию (blacklist)
- этап 3: извлечь данные из скрытой таблицы
- этап 4: декодировать base64 флаг
3. СИСТЕМА ПОДСКАЗОК:
- подсказка 1 (бесплатно): "Обратите внимание на параметр search"
- подсказка 2 (-50 очков): "Попробуйте UNION SELECT"
- подсказка 3 (-100 очков): "Таблица называется admin_secrets"
Метрики успеха: 40% команд решают за 2-4 часа
Fallback план: если слишком сложно, добавить промежуточные подсказки
Docker-изоляция — обязательный стандарт 2025:
# Пример Dockerfile для веб-задания
FROM python:3.11-slim
# Создаем непривилегированного пользователя
RUN useradd -m -s /bin/bash ctfuser
# Устанавливаем зависимости
COPY requirements.txt /app/
RUN pip install --no-cache-dir -r /app/requirements.txt
# Копируем код приложения
COPY --chown=ctfuser:ctfuser . /app/
WORKDIR /app
# Настройки безопасности
USER ctfuser
EXPOSE 8080
# Запуск с ограничениями
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "2", "--timeout", "30", "app:app"]
[Senior] Продвинутая изоляция:
# docker-compose.yml для комплексного задания
version: '3.8'
services:
web:
build: ./web
ports:
- "8080:8080"
environment:
- DB_HOST=database
- REDIS_HOST=cache
depends_on:
- database
- cache
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M
database:
image: postgres:15-alpine
environment:
- POSTGRES_DB=ctf_task
- POSTGRES_USER=ctfuser
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
secrets:
- db_password
cache:
image: redis:7-alpine
command: redis-server --maxmemory 64mb --maxmemory-policy allkeys-lru
secrets:
db_password:
file: ./secrets/db_password.txt
Автоматизированное тестирование — критически важно:
ПРИМЕР: CI/CD pipeline для тестирования CTF-задания
Язык: GitLab CI / GitHub Actions
Зависимости: Docker, pytest, security scanners
1. СТАТИЧЕСКИЙ АНАЛИЗ КОДА:
- запустить bandit (Python) или gosec (Go) для поиска уязвимостей
- проверить Dockerfile на best practices с hadolint
- сканировать зависимости на известные CVE с safety/npm audit
2. ФУНКЦИОНАЛЬНОЕ ТЕСТИРОВАНИЕ:
- поднять задание в тестовой среде
- выполнить автотесты: корректность флага, доступность сервиса
- проверить что intended solution работает
- убедиться что unintended solutions заблокированы
3. НАГРУЗОЧНОЕ ТЕСТИРОВАНИЕ:
- симулировать 100 одновременных подключений
- проверить время отклика < 2 секунд
- убедиться что контейнер не падает при спаме запросов
4. БЕЗОПАСНОСТЬ ИНФРАСТРУКТУРЫ:
- просканировать открытые порты с nmap
- проверить что нет доступа к host системе
- убедиться что логи не содержат чувствительную информацию
Критерии прохождения: все тесты зеленые + manual review от второго автора
Время выполнения pipeline: максимум 10 минут
Чек-лист безопасности для авторов:
Категория | Проверка | Критичность |
---|---|---|
Изоляция | Контейнер запускается от непривилегированного пользователя | Высокая |
Ресурсы | Ограничения по CPU/RAM/диску настроены | Высокая |
Сеть | Нет доступа к внутренним сервисам хоста | Критичная |
Логи | Флаги и секреты не попадают в логи | Средняя |
Бэкдоры | Нет скрытых способов получить флаг | Критичная |
Российские провайдеры (рекомендуемые):
Зарубежные (доступные):
ПРИМЕР: Автоматический деплой через GitLab CI
Язык: YAML (GitLab CI)
Зависимости: Docker, SSH, rsync
1. ПОДГОТОВКА ОКРУЖЕНИЯ:
- создать переменные CI_REGISTRY_USER, CI_REGISTRY_PASSWORD
- настроить SSH ключи для доступа к production серверу
- подготовить docker-compose.prod.yml с production настройками
2. СБОРКА И ПУБЛИКАЦИЯ ОБРАЗА:
- собрать Docker образ с тегом $CI_COMMIT_SHA
- запушить образ в GitLab Container Registry
- создать latest тег для стабильной версии
3. ДЕПЛОЙ НА PRODUCTION:
- подключиться к серверу по SSH
- скачать новую версию docker-compose.prod.yml
- выполнить docker-compose pull для обновления образов
- перезапустить сервисы с zero-downtime: docker-compose up -d
4. ПРОВЕРКА РАБОТОСПОСОБНОСТИ:
- дождаться запуска всех контейнеров (timeout 60 секунд)
- выполнить health check: curl http://localhost/health
- отправить уведомление в Telegram о статусе деплоя
Rollback план: если health check провален, откатиться к предыдущей версии
Время деплоя: 2-3 минуты для простого задания
Nginx конфигурация для CTF:
server {
listen 80;
server_name ctf-task.example.ru;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name ctf-task.example.ru;
ssl_certificate /etc/letsencrypt/live/ctf-task.example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ctf-task.example.ru/privkey.pem;
# Security headers для CTF
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Rate limiting против спама
limit_req_zone $binary_remote_addr zone=ctf:10m rate=10r/s;
limit_req zone=ctf burst=20 nodelay;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Таймауты для стабильности
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
}
[Senior] Система мониторинга в реальном времени:
ПРИМЕР: Мониторинг CTF-задания
Язык: Python 3.11+ + Prometheus
Зависимости: prometheus_client, psutil, requests
1. МЕТРИКИ ПРОИЗВОДИТЕЛЬНОСТИ:
- отслеживать CPU/RAM/диск каждые 30 секунд
- считать количество активных подключений
- измерять время отклика на типовые запросы
- логировать количество решений в час
2. ДЕТЕКЦИЯ АНОМАЛИЙ:
- если CPU > 80% более 5 минут → алерт в Telegram
- если время отклика > 5 секунд → проверить DoS атаку
- если 0 решений более 2 часов → возможно задание сломано
- если решений больше expected_rate * 3 → возможна утечка флага
3. АВТОМАТИЧЕСКОЕ ВОССТАНОВЛЕНИЕ:
- перезапуск контейнера при падении
- масштабирование при высокой нагрузке (Docker Swarm)
- переключение на backup сервер при критических ошибках
4. УВЕДОМЛЕНИЯ ОРГАНИЗАТОРОВ:
- Telegram бот с командами /status, /restart, /logs
- Dashboard в Grafana с ключевыми метриками
- Email алерты для критичных инцидентов
SLA цель: 99.5% uptime во время соревнования (максимум 12 минут простоя за 48 часов)
Проверено на практике — такой мониторинг спасает от 90% проблем.
Структура качественного writeup:
ПРИМЕР: Шаблон авторского writeup
Язык: Markdown
Зависимости: скриншоты, код решения
1. ВВЕДЕНИЕ И АНАЛИЗ:
- краткое описание задания и его цель
- первичный анализ: что видит участник
- инструменты которые понадобятся
- примерное время решения
2. ПОШАГОВОЕ РЕШЕНИЕ:
- каждый шаг с объяснением логики
- скриншоты ключевых моментов
- код/команды с комментариями
- объяснение почему именно такой подход
3. АЛЬТЕРНАТИВНЫЕ РЕШЕНИЯ:
- другие способы решения (если есть)
- более элегантные или быстрые подходы
- объяснение почему основной способ лучше для обучения
4. ОБРАЗОВАТЕЛЬНАЯ ЧАСТЬ:
- объяснение уязвимости в реальном мире
- методы защиты от такой атаки
- ссылки на дополнительные материалы
- похожие CVE или кейсы
5. СТАТИСТИКА И ВЫВОДЫ:
- сколько команд решили задание
- типичные ошибки участников
- фидбек от сообщества
Цель: участник должен не только получить флаг, но и понять суть уязвимости
[Middle] Пример фрагмента writeup:
## Шаг 2: Обход фильтрации SQL injection
После анализа исходного кода мы видим фильтрацию:
def is_safe_input(user_input): blocked = ['union', 'select', 'drop', 'insert', 'update'] return not any(word in user_input.lower() for word in blocked)
**Проблема:** фильтр проверяет только точные совпадения слов.
**Решение:** используем комментарии SQL для разделения ключевых слов:
' UNI//ON SEL//ECT 1,2,3,4 --
**Объяснение:** MySQL игнорирует комментарии `/**/`, поэтому `UNI/**/ON` становится `UNION`.
**В реальном мире:** такая фильтрация встречается в legacy системах. Современная защита — параметризованные запросы.
Что такое динамические флаги в CTF и как их реализовать?
Динамические флаги — это уникальные флаги для каждой команды, предотвращающие списывание. Реализация через хеширование team_id + challenge_id + секретный ключ. В CTFd используйте плагин Dynamic Value Challenge, для кастомных платформ — создайте API endpoint /api/flag/<team_id>/<challenge_id>
который генерирует флаг на лету.
Как правильно тестировать CTF-задания перед деплоем?
Обязательный чек-лист: 1) Статический анализ кода (bandit, gosec), 2) Функциональные тесты (intended solution работает), 3) Негативные тесты (unintended solutions заблокированы), 4) Нагрузочное тестирование (100 одновременных подключений), 5) Безопасность контейнера (нет доступа к хосту), 6) Manual review от второго автора. Используйте CI/CD pipeline для автоматизации.
Какие категории CTF-заданий существуют и как их разрабатывать?
Основные категории: Веб (40% заданий) — SQL injection, XSS, SSRF; Криптография (25%) — RSA, AES, hash attacks; Реверс (20%) — бинарный анализ, обфускация; Форензика (10%) — анализ дампов, логов; Pwn (5%) — buffer overflow, ROP. Выбирайте категорию под свою экспертизу, изучайте актуальные CVE, создавайте реалистичные сценарии.
Зачем нужна Docker изоляция для CTF-заданий?
Docker изоляция обеспечивает: 1) Безопасность — участники не могут повредить хост-систему, 2) Консистентность — одинаковое окружение для всех команд, 3) Масштабируемость — легко запустить множество инстансов, 4) Восстановление — быстрый перезапуск при падении, 5) Ресурсные ограничения — предотвращение DoS атак. Это стандарт 2025 года, 90% топовых CTF используют контейнеризацию.
Как написать качественный writeup для CTF-задания?
Структура: 1) Введение с анализом задания, 2) Пошаговое решение с объяснениями, 3) Альтернативные подходы, 4) Образовательная часть про уязвимость, 5) Статистика решений. Включайте скриншоты, код с комментариями, объяснение логики каждого шага. Цель — обучить, а не просто показать решение. Writeup должен быть понятен участнику среднего уровня.
Какие основные принципы хорошего CTF-задания?
Философия CTF 2025: 1) Образовательная ценность превыше искусственной сложности, 2) Реалистичность — основано на real-world уязвимостях, 3) Прогрессивность — логичное нарастание сложности, 4) Документированность — каждый шаг объясним, 5) Техническое качество — Docker изоляция, динамические флаги, мониторинг, 6) Тестируемость — автоматизированная проверка корректности.
Проблема | Симптомы | Решение | Профилактика |
---|---|---|---|
Задание недоступно | HTTP 502/503 ошибки | Перезапуск контейнера: docker-compose restart |
Health checks в docker-compose, мониторинг |
Медленная работа | Время отклика >5 сек | Проверить CPU/RAM: docker stats , оптимизировать код |
Нагрузочное тестирование перед релизом |
Утечка флага | Слишком много решений | Сменить флаг, проанализировать логи | Динамические флаги, ограничение rate limit |
Unintended solution | Неожиданный способ решения | Hotfix с дополнительной валидацией | Тщательное тестирование, code review |
DoS атака | Высокая нагрузка, падения | Nginx rate limiting, iptables блокировка IP | Правильная настройка reverse proxy |
Платформа | Плюсы | Минусы | Цена в РФ | Когда использовать |
---|---|---|---|---|
CTFd | Бесплатная, много плагинов, активное сообщество | Требует настройки, нет встроенной изоляции | 0₽ + хостинг от 1500₽/мес | Университетские CTF, средние соревнования |
CTFtime | Интеграция с рейтингом, проверенная платформа | Только для зарегистрированных CTF | Бесплатно | Официальные международные CTF |
HackerOne CTF | Профессиональная платформа, отличный UX | Платная, ограниченная кастомизация | $500+/мес | Корпоративные тренинги, коммерческие CTF |
Кастомная разработка | Полный контроль, уникальные фичи | Высокие затраты на разработку | 500,000₽+ | Крупные соревнования с особыми требованиями |
PicoCTF Platform | Образовательная направленность, хорошие туториалы | Ограниченная функциональность | 0₽ | Обучающие CTF для школьников и студентов |
Следующие шаги: Начните с простого веб-задания на SQL injection, используйте Docker для изоляции, протестируйте с командой друзей, опубликуйте writeup для получения фидбека от сообщества. Помните: качество важнее количества — лучше одно отличное задание, чем пять посредственных.
Все права защищены. © 2016 - 2025