
Kit de CTF OWASP auto-hospedado: uma máquina, uma organização gratuita no GitHub, sem dependências de nuvem
Um plano de controle auto-hospedado para eventos de aprendizagem em segurança — uma máquina, uma organização gratuita no GitHub.
Execute-o para uma universidade, uma escola secundária, um capítulo da OWASP, um meetup.
Leia 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. 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 de forma registada). Os commits seguem Conventional Commits e não transportam 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 no 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 com 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, pelo que 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 da OWASP ou um workshop de fim de semana.
Este kit remove isso. Tudo corre a partir do Docker Compose numa máquina que já tem — um portátil, um desktop de reserva, um pequeno VPS — mais uma organização gratuita no GitHub para os forks. As rubricas para todos os seis alvos vêm dentro da máquina, pelo que não há imagem privada a pedir nem código de pontuação a escrever. Nada é faturado, nada telefona 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 da OWASP, facilitadores de workshops, equipas de segurança a realizar um dia de formação interna.
Implantado e exercitado de ponta a ponta; ainda não executado para uma
coorte 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 conduz 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,
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 lote de defeitos reais foi
encontrado e corrigido — do tipo que uma suite com mocks não consegue ver.
O que não aconteceu foi um evento real: uma coorte de concorrentes a abrir PRs reais contra forks reais, ao mesmo tempo, durante horas. Essa é a diferença 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 formulação invulgar ainda pode ser lida como uma resolução — pode subvalorizar um patch correto, nunca atribuir um ponto grátis), e o perfil de carga de uma coorte completa não está testado. Detalhe e estado atual: Estado e dependências a montante.
O que faz que aqueles não fazem: formação em defesa com patch-to-score avaliada através de pull requests do GitHub, um contrato de módulo para misturar tipos de jogo num só 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 está 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 no GitHub, sem app
OAuth, nada a configurar. Precisa de Docker com Compose v2 e openssl:```sh
git clone https://github.com/OWASP/owasp-ctf-in-a-box
cd owasp-ctf-in-a-box
./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 box, a org do evento, os logins de admin, se você executa Secure Development, as credenciais do GitHub — grava o .env, executa cada passo automatizável, guia você pelos passos que exigem a UI do GitHub e retoma se você parar e voltar depois. Todo o resto (o nome do evento, quais módulos rodam, quais targets) é uma configuração de runtime em /admin, então não há arquivo de config para editar. Ele pergunta apenas o que você realmente precisa: um evento sem Secure Development não precisa de org, nem de forks, nem de imagem de scorer, e nunca é questionado sobre eles. Pré-visualize qualquer passo que altere estado 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 admin, ou quando o Secure Development está ativo sem uma org. O wizard 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 docs de deploy.
Quer os detalhes? Cada subcomando discreto, cada passo exclusivo da UI e como os dois apps do GitHub diferem:
docs/hosting.md.
Em uma cloud em vez disso? docs/aws.md (Terraform: ECS Fargate, ElastiCache e um ALB — apply sobe / destroy desce) 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 target contra o patch e a pontuação chega ao leaderboard (~30 s depois no modo poll). Seis targets, 321 desafios; o código original pontua 0, um patch correto ganha seus pontos — com gate nas duas direções. Precisa da org 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 retry. 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 case-sensitive (o card dela diz isso), com cooldown de submissão e dicas pagas opcionais. Mesma criação via /admin + bundle JSON do quiz. Também não precisa de GitHub.
AI — desafios de prompt-injection e guardrail hospedados fora da box. A página de desafio de cada participante gera para ele um link de lançamento pessoal para o site externo; um solve reporta de volta ao leaderboard, seja pelo callback do próprio 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 solve; o painel /admin com allowlist — freeze, 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 stream de atividades e métricas de engajamento — tudo em runtime, sem rebuild; e um log de auditoria com limite em cada ação de admin.
| 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. Targets e links de fork são orientados pela config do evento; o
nome do evento e o restante de sua identidade visual são configurações do painel de admin.
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 corrigem dentro do app e gravam pontos direto no Redis.
O Secure Development é corrigido fora da box: o fork do participante executa uma
GitHub Action que inicializa o target, roda 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 box 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 — solves nunca são desfeitos por uma execução falha posterior.
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 targets vulneráveis e suas rubricas
de pontuação. Os participantes escolhem um target, fazem fork da cópia da org, corrigem-no e
abrem um PR. Os desafios de cada target são suítes node:test executáveis, precificadas
por dificuldade.
As contagens são mantidas à mão e fixadas à rubrica vendorizada por
apps/web/src/lib/tests/apps-catalogue.test.ts — reconfira-as
após um bump de vendor-rubric.sh. Patches de referência
que provam que uma correção correta pontua (o gate da 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-in-a-box/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 isto.
Depois de a stack estar no ar no seu EVENT_URL:
/admin: congelar a tabela de classificação, abrir e fechar
inscrições, definir o calendário, criar perguntas de quiz, desafios clássicos
e desafios ai — e quando um participante fica bloqueado, corrigir esse
participante em vez de reiniciar o evento.docker compose logs -f sync (corre com
secure-development ativado). Todo o estado vive em volumes Docker nomeados, por isso
um reinício da máquina não perde nada../setup/ctf-setup.sh teardown arquiva os repositórios
alvo — depois desinstale a GitHub App e elimine os segredos de Actions da organização
você mesmo. Um evento sem secure-development não tem forks para arquivar.Equipas, o painel de administração, verificar o kit antes do dia, e a stack de desenvolvimento local estão todos cobertos em docs/operations.md; pré-requisitos, o transporte de pontuações, configuração OAuth e configuração do evento em docs/hosting.md.
O raciocínio completo, alternativas e trade-offs estão registados como ADRs numerados em docs/decisions.md.
Renderizado em owasp.github.io/owasp-ctf-in-a-box.
Contribuições bem-vindas — CONTRIBUTING.md cobre o ambiente de desenvolvimento, os gates de CI, e como propor um módulo; CODE_OF_CONDUCT.md aplica-se.
Os agentes devem seguir AGENTS.md. Os comandos abaixo correspondem ao CI;
make help lista os mesmos targets.
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-in-a-box/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-in-a-box/blob/main/LICENSE). O conteúdo do rubric 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.
| Target | Desafios | Pontos | Notas |
|---|
vulnerableapp | 110 | 187 | Maior target; pontuado em 8 vias paralelas |
webgoat | 69 | 137 | Build em dois estágios: Maven, depois o Dockerfile runtime-only do fork |
dvwa | 55 | 108 | Precisa de um sibling MariaDB e uma inicialização de schema |
securityshepherd | 40 | 79 | HTTPS, stack de três containers, estritamente serial |
juice-shop | 38 | 141 | O único target cuja dificuldade vai até 6 estrelas |
vampi | 9 | 16 | Autocontido; a prova ponta a ponta mais rápida |
| Total | 321 | 668 | Todo evento provisiona os seis; escolha um subconjunto em /admin → Secure Development → Targets |
| Leia isto quando estiver… | Documento |
|---|
| A montar o kit | docs/hosting.md — pré-requisitos, o assistente e cada passo discreto, como as pontuações chegam à máquina, a app GitHub OAuth, configuração do evento |
| A fazer deploy para uma cloud | 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 |
| A executar o evento | docs/operations.md — equipas, o painel de administração, os guias de organizador de quiz/classic/ai, verificação, teardown |
| A compreender o sistema | docs/architecture.md — diagrama, fluxo de dados de pontuação, chaves Redis, modelo de segurança, estratégia de testes |
| A escrever uma rubrica | docs/scorer.md — modos serve + judge, ambas as gramáticas de rubrica, autoria e build |
| A construir um novo módulo | docs/modules.md — o contrato plataforma/módulo |
| A perguntar "porque é que é assim?" | docs/decisions.md — ADRs numerados |