
Plano de controle CTF auto-hospedado para eventos de aprendizagem em segurança: registro de equipes, placar ao vivo e módulos de patch-to-score, quiz, jeopardy e desafios de IA em uma única máquina Docker Compose.
Um plano de controle auto-hospedado para eventos de aprendizagem em segurança — uma máquina, uma organização GitHub gratuita.
Execute-o para uma universidade, uma escola secundária, um capítulo OWASP, um meetup.
Leia o AGENTS.md antes de escrever código. É o manual
de operação: os comandos exatos que a CI executa, os modos de falha que este repositório
já encontrou, e as invariantes de revisão em
docs/reviewing.md. O CLAUDE.md é um apontador para
o mesmo ficheiro.
Uma alteração está pronta quando a CI está verde e todas as threads acionáveis do CodeRabbit no último commit estão resolvidas (ou recusadas com registo). Os commits seguem Conventional Commits e não incluem atribuição de IA.
Trabalho pequeno e bem especificado está marcado com
good first issue.
Novos módulos começam como uma issue, não como um PR — consulte
CONTRIBUTING.md.
Um plano de controle, não um único jogo. A máquina dá a um evento a sua espinha dorsal partilhada — uma organização GitHub, registo de equipas, um placar ao vivo, um painel de administração para organizadores, e o pipeline de pontuação que o alimenta. Os Módulos ligam conteúdo de desafios a essa espinha dorsal, e qualquer subconjunto pode correr sozinho ou em conjunto: Desenvolvimento Seguro de patch-to-score, um banco de Quiz, um tabuleiro de Jeopardy, e desafios de IA alojados externamente. O contrato de módulo é a fronteira entre a espinha dorsal e o conteúdo, por isso a máquina está construída para alojar mais módulos — forense, segurança de API, cloud — à medida que forem surgindo.
Porque existe. O módulo de Desenvolvimento Seguro ensina defesa em vez de ataque, e é uma forma genuinamente boa de ensinar codificação segura. Até agora, executar um exigia montar Vercel, Upstash, Lambda e DynamoDB, suportar a fatura da cloud, e ter acesso a uma imagem de pontuação privada. Isso é um pedido razoável para uma conferência com orçamento. É um pedido irrazoável para um curso universitário de segurança, um clube de escola secundária, uma noite de capítulo OWASP, ou um workshop de fim de semana.
Este kit remove isso. Tudo corre a partir de Docker Compose numa máquina que já tem — um portátil, um desktop de reserva, um pequeno VPS — mais uma organização GitHub gratuita para os forks. As rubricas para todos os seis alvos vêm dentro da máquina, por isso não há imagem privada para pedir nem código de pontuação para escrever. Nada é faturado, nada comunica para casa, e quando o evento termina arquiva os repositórios e para a stack.
Para quem é: qualquer pessoa que queira executar este evento e não queira tornar-se um operador de cloud para o fazer — instrutores de cursos, organizadores de clubes, líderes de capítulos OWASP, facilitadores de workshops, equipas de segurança a executar um dia de formação interno.
Implementado e exercitado de ponta a ponta; ainda não executado para um grupo real. O
caminho completo de pontuação vem no kit — o POST /score autenticado por bearer do scorer, o
workflow de pontuação autónomo para os forks, o transporte de polling — e
scripts/smoke.sh percorre todo esse pipeline contra mocks. Além disso,
o kit corre continuamente numa máquina alojada a partir do mesmo ficheiro Compose que este
repositório fornece, o GET /health reporta a revisão exata que o serve, e uma
passagem de ponta a ponta sobre essa instância ao vivo foi onde um conjunto de defeitos reais foram
encontrados e corrigidos — do tipo que uma suite com mocks não consegue ver.
O que não aconteceu é um evento real: um grupo de participantes a abrir PRs reais contra forks reais, ao mesmo tempo, durante horas. Essa é a lacuna entre "o pipeline funciona" e "o pipeline funciona com 40 pessoas". Duas ressalvas estão em aberto em vez de enterradas: o matcher de resultados do Security Shepherd tem um limite residual declarado (uma recusa com fraseamento invulgar pode ainda ser lida como uma resolução — pode subcreditar um patch correto, nunca atribuir um ponto grátis), e o perfil de carga de um grupo completo não está testado. Detalhe e estado atual: Estado e dependências a montante.
O que faz que os outros não fazem: formação em defesa patch-to-score avaliada através de pull requests do GitHub, um contrato de módulo para misturar tipos de jogo num único placar, e um plano de controle que possui de ponta a ponta — uma máquina, uma organização gratuita, sem fatura de cloud, sem telemetria.
Este projeto não é afiliado nem endossado pela OWASP Foundation. Quatro dos seis alvos vulneráveis são projetos OWASP (Juice Shop, WebGoat, Security Shepherd, VulnerableApp); DVWA e VAmPI são projetos da comunidade.
Veja-o a correr em dois minutos — sem organização GitHub, sem aplicação OAuth, nada para
configurar. Precisa de Docker com Compose v2 e openssl:```sh
git clone https://github.com/dcotelo/owasp-ctf
cd owasp-ctf
./scripts/dev-stack up
Ele grava segredos locais descartáveis, constrói as imagens do scorer e da app, sobe a stack, semeia um leaderboard de demonstração através da API de scoring real do scorer, e imprime o URL a abrir. Deverás ver o leaderboard com equipas semeadas e um gráfico de pontuação ao longo do tempo; `./scripts/dev-stack score <login> juice-shop 3` regista mais três solves em tempo real. `./scripts/dev-stack down` desmonta tudo.
**Executa um evento real** com o assistente guiado. Adiciona a **[`gh`
CLI](https://cli.github.com)** (autenticada), mais **uma organização GitHub gratuita**
se o evento incluir Secure Development; `./setup/ctf-setup.sh check` verifica
primeiro as ferramentas:```sh
./setup/ctf-setup.sh # guided, prompts for values, resumable
Ele pergunta cada valor à medida que avança — o URL da sua máquina, a organização do evento, os logins de administrador, se você executa o Secure Development, as credenciais do GitHub — grava o .env, executa cada passo automatizável, guia você pelos passos que exigem a interface do GitHub e retoma se você parar e voltar depois. Todo o resto (o nome do evento, quais módulos são executados, quais alvos) é uma configuração de runtime em /admin, então não há arquivo de configuração para editar. Ele pergunta apenas o que você realmente precisa: um evento sem Secure Development não precisa de organização, nem de forks, nem de imagem de scorer, e nunca é questionado sobre eles. Pré-visualize qualquer passo que modifique algo com --dry-run — ele narra os passos 4–9 a partir de um .env já completo, e recusa (por design) quando não há login de administrador, ou quando o Secure Development está ativado sem uma organização. O assistente encerra executando ./setup/ctf-setup.sh doctor — uma matriz de status por fork que você pode reexecutar a qualquer momento — e então oferece um deploy no fly.io opcional (padrão não), de modo que colocar o mesmo evento em um hostname público é um fluxo guiado — o hostname, um deploy pré-visualizado, depois uma confirmação — em vez de uma viagem pelos documentos de deploy.
Quer os detalhes? Cada subcomando discreto, cada passo exclusivo da interface e como os dois apps do GitHub diferem:
docs/hosting.md.
Em uma nuvem em vez disso? docs/aws.md (Terraform: ECS Fargate, ElastiCache e um ALB — apply para subir / destroy para derrubar) ou
docs/fly.md (uma máquina Fly).
Secure Development — faça fork de um app deliberadamente vulnerável, encontre a falha, corrija-a, abra um PR. Uma GitHub Action no fork executa a rubrica do alvo contra o patch e a pontuação chega ao leaderboard (~30 s depois no modo de polling). Seis alvos, 321 desafios; o código original pontua 0, um patch correto ganha seus pontos — com portão nos dois sentidos. Precisa da organização do GitHub e do pipeline de pontuação.
Quiz — perguntas de segurança de seleção única e múltipla, corrigidas no app no momento em que são respondidas (tudo ou nada na seleção múltipla), com limite de tentativas e cooldown de nova tentativa. Criadas a partir de /admin uma de cada vez ou importadas e exportadas como um único bundle JSON. Não precisa de GitHub, nem de forks, nem de pipeline.
Jeopardy — um tabuleiro de flags criadas pelos organizadores em categorias. As submissões são aparadas e normalizadas, com maiúsculas/minúsculas ignoradas a menos que uma flag seja marcada como sensível a maiúsculas/minúsculas (o card dela diz isso), com cooldown de submissão e dicas pagas opcionais. Mesma criação via /admin + bundle JSON que o quiz. Também não precisa de GitHub.
AI — desafios de prompt-injection e guardrail hospedados fora da máquina. A página de desafio de cada participante gera para ele um link de lançamento pessoal para o site externo; uma solução é reportada de volta ao leaderboard, seja pelo próprio callback daquele site ou por uma flag digitada de volta no app. Não precisa de GitHub, nem de forks, nem de pipeline.
Em torno de quaisquer módulos que você habilitar, a plataforma fornece: auto-registro de equipes com capitães, códigos de entrada e links /join/<code> (jogo solo é uma equipe de um; uma flag resolvida por vários colegas de equipe conta uma vez); o leaderboard ao vivo com um gráfico de pontuação ao longo do tempo no estilo CTFd a partir de timestamps reais por solução; o painel /admin com lista de permissões — congelamento, janelas de pontuação e registro, dicas e custos, limite de equipes, cooldowns, conteúdo dos módulos, ações de suporte por participante, um fluxo de atividades e métricas de engajamento — tudo em runtime, sem rebuild; e um log de auditoria limitado em cada ação de administrador.
| Detalhamento do participante | Navegador de desafios |
|---|---|
![]() | ![]() |
| Tabuleiro de flags do Jeopardy | Quiz |
|---|---|
![]() | ![]() |
Capturado do app do participante rodando localmente via scripts/dev-stack up
com jogadores de demonstração pré-carregados. Alvos e links de fork são orientados pela configuração do evento; o
nome do evento e o resto de sua identidade visual são configurações do painel de administração.
Uma stack Docker Compose: o Caddy termina o TLS na frente do app Next.js;
o app fala com o Redis apenas através do srh (um proxy REST compatível com Upstash) —
a rede é dividida para que nada exposto à internet tenha rota para redis:6379.
Quiz, Jeopardy e AI são corrigidos dentro do app e creditam pontos direto no Redis.
O Secure Development é corrigido fora da máquina: o fork do participante executa uma
GitHub Action que inicializa o alvo, executa a rubrica contra o patch e
publica um comentário de pontuação legível por máquina no PR. O poller sync puxa
esses comentários — zero superfície de rede de entrada, então a máquina funciona atrás de NAT e
no wifi do local (esse é o único transporte: a ingestão por push foi removida na v0.6,
veja #377). A pontuação
entra através de um único escritor auditado:
o POST /score autenticado por bearer do scorer, que valida e grava
monotonicamente — soluções nunca são desfeitas por uma execução posterior que falha.
O quadro completo — componentes, o fluxo de dados de pontuação em nove passos, o modelo de segurança — está em docs/architecture.md.
O conteúdo deste módulo é um conjunto de alvos vulneráveis e suas rubricas
de pontuação. Os participantes escolhem um alvo, fazem fork da cópia da organização, corrigem-no e
abrem um PR. Os desafios de cada alvo são suítes node:test executáveis, precificadas
por dificuldade.
As contagens são mantidas manualmente e fixadas à rubrica vendorizada por
apps/web/src/lib/tests/apps-catalogue.test.ts — verifique-as novamente
após um bump de vendor-rubric.sh. Patches de referência
que provam que uma correção correta pontua (o portão na direção positiva) ficam separadamente
em patches/.
As rubricas ficam em scorer/rubric.owasp/, vendorizadas de
OWASP-CTF/dc34-owasp-secure-development-ctf
e fixadas ao único commit upstream registrado em
scorer/rubric.owasp/PROVENANCE.md. Re-vendorize contra um commit mais novo com:```sh
./scripts/vendor-rubric.sh --all --ref
Duas formas de rubrica são suportadas ao mesmo tempo, e um único diretório de rubrica pode misturá-las: arquivos `<target>.yaml` usam a gramática declarativa de sonda de requisição/expectativa HTTP, e diretórios `<target>/tests/challenges/` usam testes executáveis precificados por `catalogue.<target>.json`. Guia de autoria:
[docs/scorer.md](https://github.com/owasp/owasp-ctf/blob/main/docs/scorer.md).
**Sobre o sigilo da rubrica.** Estas rubricas são públicas. Os alvos são de código aberto e suas soluções já estão publicadas, então o kit trata a privacidade da rubrica como proteção contra manipulação de verificações, e não contra o conhecimento das respostas — um trade-off aceito para um evento auto-hospedado. Substitua pela sua própria rubrica privada a qualquer momento:```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/ está no gitignore e reservado exatamente para isso.
Depois que a stack estiver no ar na sua EVENT_URL:
/admin: congelar o placar, abrir e fechar
inscrições, definir o cronograma, criar perguntas de quiz, desafios classic
e desafios ai — e quando um participante fica travado, corrija aquele
participante em vez de resetar o evento.docker compose logs -f sync (ele roda com
secure-development habilitado). Todo o estado vive em volumes Docker nomeados, então
um reboot da máquina não perde nada../setup/ctf-setup.sh teardown arquiva os repositórios
alvo — depois desinstale o GitHub App e apague os secrets do Actions da org
você mesmo. Um evento sem secure-development não tem forks para arquivar.Times, o painel de admin, a verificação do kit antes do dia e a stack de desenvolvimento local estão todos cobertos em docs/operations.md; pré-requisitos, o transporte de pontuação, configuração de OAuth e configuração do evento em docs/hosting.md.
O raciocínio completo, alternativas e trade-offs estão registrados como ADRs numerados em docs/decisions.md.
Renderizado em dcotelo.github.io/owasp-ctf.
Contribuições são bem-vindas — CONTRIBUTING.md cobre o ambiente de desenvolvimento, os gates de CI e como propor um módulo; CODE_OF_CONDUCT.md se aplica.
Agentes devem seguir AGENTS.md. Os comandos abaixo correspondem ao CI;
make help lista os mesmos alvos.
Cada serviço testa independentemente (Node 22 em todos):```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
Encontrou uma vulnerabilidade no próprio kit? **[SECURITY.md](https://github.com/owasp/owasp-ctf/blob/main/SECURITY.md)** — as
vulnerabilidades dos alvos são intencionais e estão fora do escopo.
## Licença e créditos
MIT — consulte [LICENSE](https://github.com/owasp/owasp-ctf/blob/main/LICENSE). O conteúdo da rubrica em `scorer/rubric.owasp/`
é proveniente do evento upstream
[OWASP-CTF](https://github.com/OWASP-CTF/dc34-owasp-secure-development-ctf), fixado no commit em `scorer/rubric.owasp/PROVENANCE.md` — este kit
existe porque esse evento valia a pena ser executado mais de uma vez. Os alvos
vulneráveis não são provenientes do upstream: os eventos fazem fork deles a partir dos seus próprios upstreams
([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)), e cada um mantém a sua própria licença.
OWASP® é uma marca registada da OWASP Foundation; este projeto não está
afiliado nem é endossado por ela.
| Alvo | Desafios | Pontos | Notas |
|---|
vulnerableapp | 110 | 187 | Maior alvo; pontuado em 8 vias paralelas |
webgoat | 69 | 137 | Build em dois estágios: Maven, depois o Dockerfile somente-runtime do fork |
dvwa | 55 | 108 | Precisa de um sibling MariaDB e uma inicialização de schema |
securityshepherd | 40 | 79 | HTTPS, stack de três contêineres, estritamente serial |
juice-shop | 38 | 141 | O único alvo cuja dificuldade chega a 6 estrelas |
vampi | 9 | 16 | Autocontido; a prova ponta a ponta mais rápida |
| Total | 321 | 668 | Todo evento provisiona todos os seis; escolha um subconjunto em /admin → Secure Development → Targets |
| Leia isto quando você estiver… | Documento |
|---|
| Montando o kit | docs/hosting.md — pré-requisitos, o wizard e cada passo discreto, como as pontuações chegam à máquina, o app GitHub OAuth, configuração do evento |
| Fazendo deploy para a nuvem | docs/aws.md (Terraform: ECS Fargate + ElastiCache + ALB) · docs/fly.md (uma máquina Fly) |
| Prestes a abrir as portas | docs/security-checklist.md — a verificação pré-evento de uma página |
| Executando o evento | docs/operations.md — times, o painel de admin, os guias de organizador de quiz/classic/ai, verificação, teardown |
| Entendendo o sistema | docs/architecture.md — diagrama, fluxo de dados de pontuação, chaves Redis, modelo de segurança, estratégia de testes |
| Escrevendo uma rubrica | docs/scorer.md — modos serve + judge, ambas as gramáticas de rubrica, autoria e build |
| Construindo um novo módulo | docs/modules.md — o contrato plataforma/módulo |
| Perguntando "por que é assim?" | docs/decisions.md — ADRs numerados |