
Самостоятельно размещаемый CTF control plane для мероприятий по обучению безопасности: регистрация команд, живая таблица лидеров и модули patch-to-score, quiz, jeopardy и AI-задач на одном хосте Docker Compose.
Самостоятельно размещаемая панель управления для мероприятий по обучению безопасности — одна машина, одна бесплатная организация GitHub.
Запускайте её для университета, школы, отделения OWASP, митапа.
Прочитайте AGENTS.md перед тем, как писать код. Это руководство
по эксплуатации: точные команды, которые запускает CI, режимы отказа, с
которыми этот репозиторий уже сталкивался, и инварианты ревью в
docs/reviewing.md. CLAUDE.md — это указатель на
тот же файл.
Изменение готово, когда CI зелёный и каждый actionable-тред CodeRabbit на последнем коммите разрешён (или отклонён с записью в протоколе). Коммиты следуют Conventional Commits и не содержат указания на авторство ИИ.
Небольшая, хорошо специфицированная работа помечена тегом
good first issue.
Новые модули начинаются с issue, а не с PR — см.
CONTRIBUTING.md.
Панель управления, а не отдельная игра. Машина даёт мероприятию общий хребет — организацию GitHub, регистрацию команд, живую таблицу лидеров, панель администратора для организаторов и конвейер подсчёта очков, который её питает. Модули подключают контент испытаний к этому хребту, и любое подмножество может работать отдельно или вместе: patch-to-score Secure Development, банк Quiz, доска Jeopardy и внешне размещённые AI-испытания. Контракт модуля — это граница между хребтом и контентом, поэтому машина построена так, чтобы размещать дальнейшие модули — форензику, безопасность API, облако — по мере их появления.
Зачем это существует. Модуль Secure Development учит защите, а не атаке, и это по-настоящему хороший способ учить безопасному программированию. До сих пор запуск такого мероприятия означал развёртывание Vercel, Upstash, Lambda и DynamoDB, оплату облачных счетов и наличие доступа к приватному образу для подсчёта очков. Это разумная просьба для конференции с бюджетом. Это неразумная просьба для университетского курса по безопасности, школьного клуба, вечера отделения OWASP или выходного воркшопа.
Этот набор устраняет её. Всё работает из Docker Compose на одной машине, которая у вас уже есть — ноутбук, запасной десктоп, небольшой VPS — плюс одна бесплатная организация GitHub для форков. Рубрики для всех шести целей поставляются внутри набора, так что нет приватного образа, который нужно запрашивать, и нет кода подсчёта очков, который нужно писать. Ничего не тарифицируется, ничего не отправляет данные наружу, и когда мероприятие заканчивается, вы архивируете репозитории и останавливаете стек.
Для кого это: для всех, кто хочет провести это мероприятие и не хочет становиться облачным оператором ради этого — преподавателей курсов, организаторов клубов, руководителей отделений OWASP, ведущих воркшопов, команд по безопасности, проводящих внутренний день обучения.
Развёрнуто и проверено от начала до конца; ещё не запускалось для реальной
группы. Полный путь подсчёта очков поставляется внутри набора — POST /score
скоринга с bearer-аутентификацией, самодостаточный workflow подсчёта очков для
форков, транспорт опроса — и scripts/smoke.sh прогоняет весь этот конвейер
против моков. Помимо этого, набор непрерывно работает на размещённой машине
из того же файла Compose, который поставляет этот репозиторий, GET /health
сообщает точную ревизию, обслуживающую его, и сквозной прогон по этому живому
экземпляру — это то, где была найдена и исправлена целая партия реальных
дефектов — таких, которых не видит набор с моками.
Чего не произошло — так это реального мероприятия: группы участников, открывающих реальные PR против реальных форков, одновременно, в течение часов. Это разрыв между «конвейер работает» и «конвейер работает при 40 человеках». Две оговорки открыты, а не спрятаны: сопоставитель результатов Security Shepherd имеет заявленное остаточное ограничение (необычно сформулированный отказ всё ещё может прочитаться как решение — он может недоначислить очки за правильный патч, но никогда не начислит бесплатное очко), и профиль нагрузки полной группы не протестирован. Подробности и текущее состояние: Статус и вышестоящие зависимости.
Что он делает такого, чего не делают те: обучение защите по схеме patch-to-score с оценкой через pull request'ы GitHub, контракт модуля для смешивания типов игр в одной таблице лидеров и панель управления, которой вы владеете от начала до конца — одна машина, одна бесплатная организация, никаких облачных счетов, никакой телеметрии.
Этот проект не связан с OWASP Foundation и не одобрен ею. Четыре из шести уязвимых целей — это проекты OWASP (Juice Shop, WebGoat, Security Shepherd, VulnerableApp); DVWA и VAmPI — это проекты сообщества.
Увидеть это в работе за две минуты — без организации GitHub, без
OAuth-приложения, ничего настраивать не нужно. Вам нужны Docker с Compose v2
и openssl:```sh
git clone https://github.com/dcotelo/owasp-ctf
cd owasp-ctf
./scripts/dev-stack up
Он записывает одноразовые локальные секреты, собирает образы scorer и приложения, поднимает стек, наполняет демонстрационную таблицу лидеров через реальный API подсчёта очков scorer и выводит URL для открытия. Вы должны увидеть таблицу лидеров с добавленными командами и график изменения счёта во времени; `./scripts/dev-stack score <login> juice-shop 3` добавляет ещё три решения в реальном времени. `./scripts/dev-stack down` останавливает и удаляет стек.
**Проведите реальное мероприятие** с помощью пошагового мастера. Добавьте **[`gh`
CLI](https://cli.github.com)** (аутентифицированный), а также **одну бесплатную организацию GitHub**, если мероприятие включает Secure Development; `./setup/ctf-setup.sh check` сначала проверяет инструментарий:```sh
./setup/ctf-setup.sh # guided, prompts for values, resumable
Он запрашивает каждое значение по ходу дела — URL вашего сервера, организатора мероприятия, логины администратора, используете ли вы Secure Development, учётные данные GitHub — записывает .env, выполняет все автоматизируемые шаги, проводит вас через шаги в GitHub-UI и возобновляет работу, если вы остановитесь и вернётесь позже. Всё остальное (название мероприятия, какие модули запускаются, какие цели) — это настройка /admin во время выполнения, поэтому файла конфигурации для редактирования нет. Он спрашивает только то, что вам действительно нужно: мероприятию без Secure Development не нужны ни организация, ни форки, ни образ scorer'а, и о них его никогда не спрашивают. Предпросмотр любого изменяющего шага с --dry-run — он описывает шаги 4–9 из уже заполненного .env и отказывается работать (по замыслу), когда нет логина администратора или когда Secure Development включён без организации. Мастер завершается запуском ./setup/ctf-setup.sh doctor — матрицы статуса по каждому форку, которую можно перезапускать в любое время — а затем предлагает опциональный деплой на fly.io (по умолчанию нет), так что размещение того же мероприятия на публичном домене — это управляемый процесс: домен, предпросмотр деплоя, затем подтверждение — а не поход по документации деплоя.
Хотите подробности? Каждая отдельная подкоманда, каждый шаг только через UI и чем отличаются два GitHub-приложения:
docs/hosting.md.
Вместо этого в облаке? docs/aws.md (Terraform: ECS Fargate, ElastiCache и ALB — apply для поднятия / destroy для сноса) или
docs/fly.md (одна машина Fly).
Secure Development — форкните намеренно уязвимое приложение, найдите изъян, исправьте его, откройте PR. GitHub Action в форке прогоняет рубрику цели против патча, и результат попадает на таблицу лидеров (~30 с позже в режиме опроса). Шесть целей, 321 задание; базовый результат — 0, корректный патч приносит свои очки — с ограничением в обе стороны. Требует GitHub-организацию и конвейер подсчёта очков.
Quiz — вопросы по безопасности с одиночным и множественным выбором, оцениваемые в приложении в момент ответа (всё-или-ничего при множественном выборе), с ограничением числа попыток и задержкой перед повтором. Создаются из /admin по одному или импортируются и экспортируются одним JSON-пакетом. Не требует GitHub, форков, конвейера.
Jeopardy — доска флагов, созданных организатором, по категориям. Ответы обрезаются и нормализуются, регистр прощается, если флаг не помечен как регистрозависимый (об этом сказано на его карточке), с задержкой между отправками и опциональными платными подсказками. То же создание через /admin + JSON-пакет, что и в quiz. GitHub тоже не нужен.
AI — задания на prompt-injection и guardrail, размещённые вне сервера. Страница задания каждого участника генерирует для него персональную ссылку запуска на внешний сайт; решение сообщается обратно на таблицу лидеров — либо через собственный callback того сайта, либо через флаг, введённый обратно в приложение. Не требует GitHub, форков, конвейера.
Вокруг любых включённых вами модулей платформа предоставляет: самостоятельную регистрацию команд с капитанами, кодами присоединения и ссылками /join/<code> (соло-игра — это команда из одного человека; флаг, решённый несколькими товарищами по команде, считается один раз); живую таблицу лидеров с графиком набора очков во времени в стиле CTFd на основе реальных временных меток каждого решения; панель /admin с белым списком — заморозка, окна подсчёта очков и регистрации, подсказки и их стоимость, лимит команд, задержки, содержимое модулей, действия поддержки по каждому участнику, поток активности и метрики вовлечённости — всё во время выполнения, без пересборки; и журнал аудита с ограничением по каждому действию администратора.
| Разбивка по участнику | Браузер заданий |
|---|---|
![]() | ![]() |
| Доска флагов Jeopardy | Quiz |
|---|---|
![]() | ![]() |
Captured from the contestant app running locally via scripts/dev-stack up
with seeded demo players. Targets and fork links are event-config driven; the
event name and the rest of its branding are admin-panel settings.
Один стек Docker Compose: Caddy завершает TLS перед приложением Next.js;
приложение общается с Redis только через srh (REST-прокси, совместимый с Upstash) —
сеть разделена так, что ничто, обращённое в интернет, не имеет маршрута к redis:6379.
Quiz, Jeopardy и AI оцениваются внутри приложения и записывают очки напрямую в Redis.
Secure Development оценивается вне сервера: форк участника запускает
GitHub Action, который поднимает цель, прогоняет рубрику против патча и
публикует машиночитаемый комментарий с результатом на PR. Опрашивающий процесс sync забирает
эти комментарии — нулевая входящая сетевая поверхность, так что сервер работает за NAT и
на wifi площадки (это единственный транспорт: push-приём был удалён в v0.6,
см. #377). Результат входит через единственного аудируемого писателя:
POST /score scorer'а с bearer-аутентификацией, который валидирует и записывает
монотонно — решения никогда не отменяются более поздним неудачным запуском.
Полная картина — компоненты, девятишаговый поток данных результатов, модель безопасности — в docs/architecture.md.
Содержимое этого модуля — набор уязвимых целей и их рубрик
подсчёта очков. Участники выбирают цель, форкают копию организации, патчат её и
открывают PR. Задания каждой цели — это исполняемые наборы node:test, оценённые
по сложности.
Counts are maintained by hand and pinned to the vendored rubric by
apps/web/src/lib/tests/apps-catalogue.test.ts — re-check them
after a vendor-rubric.sh bump. Reference patches
that prove a correct fix scores (the positive-direction gate) live separately
under patches/.
Рубрики находятся в scorer/rubric.owasp/, вендоренные из
OWASP-CTF/dc34-owasp-secure-development-ctf
и привязанные к единственному upstream-коммиту, записанному в
scorer/rubric.owasp/PROVENANCE.md. Повторно вендорьте против более нового коммита с помощью:```sh
./scripts/vendor-rubric.sh --all --ref
Поддерживаются одновременно две формы рубрик, и один каталог рубрик может смешивать их: файлы `<target>.yaml` используют декларативную грамматику проб HTTP-запросов/ожиданий, а каталоги `<target>/tests/challenges/` используют исполняемые тесты, оцениваемые через `catalogue.<target>.json`. Руководство по написанию: [docs/scorer.md](https://github.com/owasp/owasp-ctf/blob/main/docs/scorer.md).
**О секретности рубрик.** Эти рубрики публичны. Цели имеют открытый исходный код, а их решения уже опубликованы, поэтому набор рассматривает приватность рубрик как защиту от подгонки проверок, а не от знания ответов — приемлемый компромисс для самостоятельно размещаемого мероприятия. Переопределить собственной приватной рубрикой можно в любой момент:```sh
cp -r /path/to/private-rubric scorer/rubric
docker build -t ghcr.io/<org>/score:latest --build-arg RUBRIC_DIR=rubric scorer/
scorer/rubric/ находится в gitignore и зарезервирован именно для этого.
Когда стек поднят по вашему EVENT_URL:
/admin: замораживают таблицу лидеров, открывают и закрывают
регистрацию, задают расписание, создают вопросы для quiz, classic
и ai челленджей — и когда один участник застревает, чинят именно этого
участника, а не сбрасывают всё мероприятие.docker compose logs -f sync (он запускается с
включённым secure-development). Всё состояние хранится в именованных Docker-томах, так что
перезагрузка машины ничего не теряет../setup/ctf-setup.sh teardown архивирует целевые
репозитории — затем удалите GitHub App и удалите секреты Actions организации
самостоятельно. У мероприятия без secure-development нет форков для архивации.Команды, панель администратора, проверка кита перед днём и локальный dev-стек описаны в docs/operations.md; предварительные требования, транспорт очков, настройка OAuth и конфигурация мероприятия — в docs/hosting.md.
Полное обоснование, альтернативы и компромиссы записаны в виде нумерованных ADR в docs/decisions.md.
Отрендерено на dcotelo.github.io/owasp-ctf.
Вклад приветствуется — CONTRIBUTING.md описывает среду разработки, гейты CI и то, как предложить модуль; применяется CODE_OF_CONDUCT.md.
Агентам следует следовать AGENTS.md. Команды ниже соответствуют CI;
make help перечисляет те же цели.
Каждый сервис тестируется независимо (Node 22 везде):```sh (cd sync && npm ci && npm test) (cd scorer && npm ci && npm test && node tools/vacuous-sweep.mjs) ./scripts/acceptance-scorer.sh # from the repo root — the script lives in scripts/ (cd apps/web && corepack pnpm install --frozen-lockfile && corepack pnpm lint && corepack pnpm test) ./scripts/smoke.sh # the full poll pipeline, end to end
Нашли уязвимость в самом ките? **[SECURITY.md](https://github.com/owasp/owasp-ctf/blob/main/SECURITY.md)** — уязвимости целевых приложений намеренны и вне области охвата.
## Лицензия и благодарности
MIT — см. [LICENSE](https://github.com/owasp/owasp-ctf/blob/main/LICENSE). Содержимое рубрики в `scorer/rubric.owasp/`
взято из апстрима события
[OWASP-CTF](https://github.com/OWASP-CTF/dc34-owasp-secure-development-ctf),
зафиксированного на коммите в `scorer/rubric.owasp/PROVENANCE.md` — этот кит
существует потому, что то событие стоило провести более одного раза. Уязвимые
целевые приложения не вендорены: события форкают их из собственных апстримов
([Juice Shop](https://github.com/juice-shop/juice-shop),
[WebGoat](https://github.com/WebGoat/WebGoat),
[DVWA](https://github.com/digininja/DVWA),
[Security Shepherd](https://github.com/OWASP/SecurityShepherd),
[VulnerableApp](https://github.com/SasanLabs/VulnerableApp),
[VAmPI](https://github.com/erev0s/VAmPI)), и каждое сохраняет собственную лицензию.
OWASP® является зарегистрированной торговой маркой OWASP Foundation; этот проект
не связан с ней и не одобрен ею.
| Цель | Задания | Очки | Примечания |
|---|
vulnerableapp | 110 | 187 | Крупнейшая цель; оценивается с параллелизмом 8 |
webgoat | 69 | 137 | Двухэтапная сборка: Maven, затем runtime-only Dockerfile форка |
dvwa | 55 | 108 | Требует соседний MariaDB и инициализацию схемы |
securityshepherd | 40 | 79 | HTTPS, стек из трёх контейнеров, строго последовательно |
juice-shop | 38 | 141 | Единственная цель, чья сложность доходит до 6 звёзд |
vampi | 9 | 16 | Самодостаточна; самое быстрое сквозное доказательство |
| Всего | 321 | 668 | Каждое мероприятие разворачивает все шесть; выберите подмножество в /admin → Secure Development → Targets |
| Читайте это, когда вы… | Документ |
|---|
| Разворачиваете кит | docs/hosting.md — предварительные требования, мастер и каждый отдельный шаг, как очки достигают машины, приложение GitHub OAuth, конфигурация мероприятия |
| Разворачиваете в облаке | docs/aws.md (Terraform: ECS Fargate + ElastiCache + ALB) · docs/fly.md (одна машина Fly) |
| Собираетесь открыть двери | docs/security-checklist.md — одностраничный предмероприятийный обход |
| Проводите мероприятие | docs/operations.md — команды, панель администратора, руководства организатора quiz/classic/ai, проверка, teardown |
| Понимаете систему | docs/architecture.md — диаграмма, поток данных очков, ключи Redis, модель безопасности, стратегия тестирования |
| Пишете рубрику | docs/scorer.md — режимы serve + judge, обе грамматики рубрик, написание и сборка |
| Создаёте новый модуль | docs/modules.md — контракт платформы/модуля |
| Задаёте вопрос «почему так?» | docs/decisions.md — нумерованные ADR |