
Kit CTF OWASP self-hosted: una macchina, una org GitHub gratuita, nessuna dipendenza dal cloud
Un control plane self-hosted per eventi di apprendimento sulla sicurezza — una macchina, una org GitHub gratuita.
Organizzalo per un'università, una scuola superiore, un capitolo OWASP, un meetup.
Leggi AGENTS.md prima di scrivere codice. È il manuale
operativo: i comandi esatti che esegue la CI, le modalità di fallimento che
questo repo ha già incontrato e le invarianti di revisione in
docs/reviewing.md. CLAUDE.md è un puntatore allo
stesso file.
Una modifica è pronta quando la CI è verde e ogni thread CodeRabbit azionabile sull'ultimo commit è risolto (o rifiutato a verbale). I commit seguono i Conventional Commits e non portano attribuzione AI.
Il lavoro piccolo e ben specificato è etichettato
good first issue.
I nuovi moduli iniziano come issue, non come PR — vedi
CONTRIBUTING.md.
Un control plane, non un singolo gioco. La macchina fornisce a un evento la sua spina dorsale condivisa — una org GitHub, la registrazione dei team, una classifica live, un pannello di amministrazione per gli organizzatori e la pipeline di scoring che lo alimenta. I moduli innestano contenuti di sfida in quella spina dorsale, e qualsiasi sottoinsieme può girare da solo o insieme: patch-to-score Secure Development, un archivio Quiz, un tabellone Jeopardy e sfide AI ospitate esternamente. Il contratto dei moduli è il confine tra spina dorsale e contenuto, quindi la macchina è costruita per ospitare ulteriori moduli — forensics, API-security, cloud — man mano che arrivano.
Perché esiste. Il modulo Secure Development insegna la difesa anziché l'attacco, ed è un modo genuinamente valido per insegnare il secure coding. Fino ad ora, eseguirne uno significava mettere in piedi Vercel, Upstash, Lambda e DynamoDB, sostenere la bolletta cloud e avere accesso a una immagine di scoring privata. È una richiesta ragionevole per una conferenza con un budget. È una richiesta irragionevole per un corso universitario di sicurezza, un club di scuola superiore, una serata di un capitolo OWASP o un workshop del fine settimana.
Questo kit la elimina. Tutto gira da Docker Compose su una macchina che hai già — un laptop, un desktop di riserva, un piccolo VPS — più una org GitHub gratuita per i fork. Le rubriche per tutti e sei i target sono incluse nella macchina, quindi non c'è nessuna immagine privata da richiedere e nessun codice di scoring da scrivere. Nulla viene fatturato, nulla telefona a casa, e quando l'evento finisce archivi i repo e fermi lo stack.
A chi è destinato: a chiunque voglia organizzare questo evento e non voglia diventare un operatore cloud per farlo — docenti, organizzatori di club, lead di capitoli OWASP, facilitatori di workshop, team di sicurezza che organizzano una giornata di formazione interna.
Distribuito ed esercitato end to end; non ancora eseguito per una coorte reale. L'intero
percorso di scoring è incluso nel kit — il POST /score con autenticazione bearer dello scorer, il
workflow di scoring self-contained per i fork, il trasporto poll — e
scripts/smoke.sh guida l'intera pipeline contro dei mock. Oltre a ciò,
il kit gira in modo continuo su una macchina ospitata dallo stesso file Compose che questo
repo distribuisce, GET /health riporta l'esatta revisione che lo serve, e un
passaggio end-to-end su quell'istanza live è dove è stato trovato e corretto un gruppo di difetti reali — del
tipo che una suite con mock non può vedere.
Ciò che non è accaduto è un evento reale: una coorte di concorrenti che aprono vere PR contro fork reali, tutti insieme, per ore. Questo è il divario tra "la pipeline funziona" e "la pipeline funziona con 40 persone". Due avvertenze sono aperte anziché sepolte: il matcher dei risultati di Security Shepherd ha un limite residuo dichiarato (un rifiuto formulato in modo insolito può comunque essere letto come una soluzione — può sottostimare una patch corretta, mai assegnare un punto gratis), e il profilo di carico di una coorte completa non è testato. Dettagli e stato attuale: Stato e dipendenze upstream.
Ciò che fa e che quelli non fanno: formazione alla difesa patch-to-score valutata attraverso pull request GitHub, un contratto di moduli per mescolare tipi di gioco su un'unica classifica e un control plane che possiedi end to end — una macchina, una org gratuita, nessuna bolletta cloud, nessuna telemetria.
Questo progetto non è affiliato né approvato dalla OWASP Foundation. Quattro dei sei target vulnerabili sono progetti OWASP (Juice Shop, WebGoat, Security Shepherd, VulnerableApp); DVWA e VAmPI sono progetti della community.
Vedilo in esecuzione in due minuti — nessuna org GitHub, nessuna app OAuth, nulla da
configurare. Ti servono Docker con 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
Scrive segreti locali usa e getta, costruisce le immagini dello scorer e dell'app, avvia lo stack, popola una classifica demo tramite la vera API di scoring dello scorer e stampa l'URL da aprire. Dovresti vedere la classifica con i team inseriti e un grafico del punteggio nel tempo; `./scripts/dev-stack score <login> juice-shop 3` registra altri tre solve in tempo reale. `./scripts/dev-stack down` smonta tutto.
**Esegui un evento reale** con la procedura guidata. Aggiungi la **[`gh`
CLI](https://cli.github.com)** (autenticata), più **una org GitHub gratuita**
se l'evento prevede Secure Development; `./setup/ctf-setup.sh check` verifica
prima gli strumenti:```sh
./setup/ctf-setup.sh # guided, prompts for values, resumable
Ti chiede ogni valore man mano che procede — l'URL della tua box, l'organizzazione dell'evento, le credenziali di accesso admin, se esegui Secure Development, le credenziali GitHub — scrive
.env, esegue
ogni passaggio automatizzabile, ti guida attraverso quelli che richiedono la GitHub-UI, e riprende se ti fermi e torni. Tutto il resto (il nome dell'evento, quali moduli vengono eseguiti, quali target) è un'impostazione runtime in /admin, quindi non c'è alcun file di configurazione da modificare. Chiede solo ciò che ti serve effettivamente: un evento senza Secure
Development non necessita di org, né di fork, né di immagine dello scorer, e non viene mai chiesto nulla al riguardo. Anteprima di qualsiasi passaggio che modifica lo stato con --dry-run — narra i passaggi
4–9 da un .env già completo, e rifiuta (per design) quando
non c'è un login admin, o quando Secure Development è attivo senza org. Il
wizard si chiude eseguendo ./setup/ctf-setup.sh doctor — una matrice di stato per-fork che puoi rieseguire in qualsiasi momento — e poi offre un opzionale deploy su fly.io
(predefinito no), così mettere lo stesso evento su un hostname pubblico è un
flusso guidato — l'hostname, un deploy in anteprima, poi una conferma — piuttosto
che un viaggio attraverso la documentazione di deploy.
Vuoi i dettagli? Ogni singolo sottocomando, ogni passaggio solo-UI, e come
differiscono le due GitHub app:
docs/hosting.md.
In un cloud invece? docs/aws.md (Terraform: ECS Fargate,
ElastiCache e un ALB — apply per salire / destroy per scendere) oppure
docs/fly.md (una macchina Fly).
Secure Development — fai il fork di un'app deliberatamente vulnerabile, trova il difetto, correggilo con una patch, apri una PR. Una GitHub Action nel fork esegue la rubric del target contro la patch e il punteggio arriva sulla classifica (~30 s dopo in modalità poll). Sei target, 321 challenge; la versione stock totalizza 0, una patch corretta guadagna i suoi punti — vincolato in entrambe le direzioni. Richiede l'organizzazione GitHub e la pipeline di scoring.
Quiz — domande di sicurezza a selezione singola e multipla, valutate nell'app
nel momento stesso in cui vengono risposte (tutto-o-niente sulla selezione multipla), con un limite di tentativi
e un cooldown per il retry. Create da /admin una alla volta oppure importate ed
esportate come un unico bundle JSON. Non richiede GitHub, né fork, né pipeline.
Jeopardy — una board di flag create dagli organizzatori, suddivise in
categorie. Le submission vengono ripulite e normalizzate, il casing viene perdonato a meno che una
flag non sia contrassegnata come case-sensitive (la sua card lo indica), con un cooldown per le submission
e suggerimenti opzionali a pagamento. Stessa creazione via /admin + bundle JSON del quiz.
Non richiede GitHub neanche questo.
AI — challenge di prompt-injection e guardrail ospitate all'esterno della box. La pagina della challenge di ogni concorrente genera per lui un link di lancio personale verso il sito esterno; una soluzione viene riportata alla classifica, o tramite il callback del sito stesso o tramite una flag digitata nell'app. Non richiede GitHub, né fork, né pipeline.
Attorno a qualunque modulo tu abiliti, la piattaforma fornisce: auto-registrazione
dei team con capitani, codici di adesione e link /join/<code> (il gioco in solitaria
è un team di uno; una flag risolta da più compagni di squadra conta una volta); la
classifica live con un grafico del punteggio nel tempo in stile CTFd basato su timestamp reali per-solve; il
pannello /admin con allowlist — freeze, finestre di scoring e registrazione, suggerimenti e costi, cap
dei team, cooldown, contenuti dei moduli, azioni di supporto per-concorrente, uno stream di attività e metriche di engagement — tutto a runtime, senza
rebuild; e un log di audit con cap su ogni azione admin.
| Dettaglio concorrente | Browser delle challenge |
|---|---|
![]() | ![]() |
| Board delle flag 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.
Un unico stack Docker Compose: Caddy termina il TLS davanti all'app Next.js;
l'app comunica con Redis solo attraverso srh (un proxy REST compatibile con Upstash) —
la rete è separata in modo che nulla esposto a internet abbia una rotta verso redis:6379.
Quiz, Jeopardy e AI valutano all'interno dell'app e depositano i punti direttamente su Redis.
Secure Development viene valutato all'esterno della box: il fork del concorrente esegue una
GitHub Action che avvia il target, esegue la rubric contro la patch, e
pubblica un commento di punteggio leggibile dalla macchina sulla PR. Il poller sync preleva
quei commenti — zero superficie di rete in ingresso, così la box funziona dietro NAT e
sul wifi della sede (questo è l'unico trasporto: l'ingest push è stato rimosso nella v0.6,
vedi #377). Il punteggio
entra attraverso un unico writer sottoposto ad audit:
il POST /score autenticato con bearer dello scorer, che valida e scrive
in modo monotono — le soluzioni non vengono mai annullate da un'esecuzione fallita successiva.
Il quadro completo — componenti, il flusso dati del punteggio in nove passaggi, il modello di sicurezza — è in docs/architecture.md.
Il contenuto di questo modulo è un insieme di target vulnerabili e le loro
rubric di scoring. I concorrenti scelgono un target, fanno il fork della copia dell'org, lo correggono con una patch, e
aprono una PR. Le challenge di ogni target sono suite node:test eseguibili, prezzate
per difficoltà.
I conteggi sono mantenuti a mano e vincolati alla rubric vendorizzata da
apps/web/src/lib/tests/apps-catalogue.test.ts — ricontrollali
dopo un aggiornamento di vendor-rubric.sh. Le patch di riferimento
che dimostrano che una correzione corretta totalizza punti (il gate in direzione positiva) si trovano separatamente
sotto patches/.
Le rubric si trovano in scorer/rubric.owasp/, vendorizzate da
OWASP-CTF/dc34-owasp-secure-development-ctf
e vincolate al singolo commit upstream registrato in
scorer/rubric.owasp/PROVENANCE.md. Ri-vendorizza contro un commit più recente con:```sh
./scripts/vendor-rubric.sh --all --ref
Sono supportate contemporaneamente due forme di rubric, e una singola directory di rubric può mescolarle: i file `<target>.yaml` usano la grammatica dichiarativa di probe richiesta/attesa HTTP, mentre le directory `<target>/tests/challenges/` usano test eseguibili valutati tramite `catalogue.<target>.json`. Guida alla scrittura:
[docs/scorer.md](https://github.com/owasp/owasp-ctf-in-a-box/blob/main/docs/scorer.md).
**Sulla segretezza delle rubric.** Queste rubric sono pubbliche. I target sono open source e le loro soluzioni sono già pubblicate, quindi il kit tratta la riservatezza delle rubric come protezione contro il check-gaming piuttosto che contro la conoscenza delle risposte — un compromesso accettato per un evento self-hosted. Sostituisci in qualsiasi momento con la tua rubric privata:```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/ è in gitignore e riservato esattamente a questo scopo.
Una volta che lo stack è attivo al tuo EVENT_URL:
/admin: congelano la classifica, aprono e chiudono
la registrazione, impostano il programma, creano domande per il quiz, sfide classic
e sfide ai — e quando un concorrente si blocca, sistemano quel singolo
concorrente invece di resettare l'evento.docker compose logs -f sync (viene eseguito con
secure-development abilitato). Tutto lo stato risiede in volumi Docker nominati, quindi
un riavvio della macchina non perde nulla../setup/ctf-setup.sh teardown archivia i repository
target — poi disinstalla la GitHub App ed elimina i secret delle Actions dell'organizzazione
manualmente. Un evento senza secure-development non ha fork da archiviare.Squadre, il pannello di amministrazione, la verifica del kit prima del giorno e lo stack di sviluppo locale sono tutti trattati in docs/operations.md; prerequisiti, il trasporto dei punteggi, la configurazione OAuth e la configurazione dell'evento in docs/hosting.md.
Il ragionamento completo, le alternative e i compromessi sono registrati come ADR numerati in docs/decisions.md.
Renderizzato su owasp.github.io/owasp-ctf-in-a-box.
Contributi benvenuti — CONTRIBUTING.md copre l'ambiente di sviluppo, i gate della CI e come proporre un modulo; si applica CODE_OF_CONDUCT.md.
Gli agenti dovrebbero seguire AGENTS.md. I comandi seguenti corrispondono alla CI;
make help elenca gli stessi target.
Ogni servizio viene testato in modo indipendente (Node 22 ovunque):```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
Hai trovato una vulnerabilità nel kit stesso? **[SECURITY.md](https://github.com/owasp/owasp-ctf-in-a-box/blob/main/SECURITY.md)** — le vulnerabilità dei target sono intenzionali e fuori ambito.
## Licenza e crediti
MIT — vedi [LICENSE](https://github.com/owasp/owasp-ctf-in-a-box/blob/main/LICENSE). Il contenuto della rubrica sotto `scorer/rubric.owasp/`
è vendored dall'evento upstream
[OWASP-CTF](https://github.com/OWASP-CTF/dc34-owasp-secure-development-ctf), fissato al commit in `scorer/rubric.owasp/PROVENANCE.md` — questo kit
esiste perché quell'evento valeva la pena di essere eseguito più di una volta. I target vulnerabili non sono vendored: gli eventi li fork dai loro upstream
([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 ciascuno mantiene la propria licenza.
OWASP® è un marchio registrato della OWASP Foundation; questo progetto non è
affiliato né approvato da essa.
| Target | Challenge | Punti | Note |
|---|
vulnerableapp | 110 | 187 | Target più grande; valutato in parallelo su 8 vie |
webgoat | 69 | 137 | Build in due fasi: Maven, poi il Dockerfile runtime-only del fork |
dvwa | 55 | 108 | Richiede un sibling MariaDB e un'inizializzazione dello schema |
securityshepherd | 40 | 79 | HTTPS, stack a tre container, strettamente seriale |
juice-shop | 38 | 141 | L'unico target la cui difficoltà arriva a 6 stelle |
vampi | 9 | 16 | Autocontenuto; la prova end-to-end più rapida |
| Totale | 321 | 668 | Ogni evento provisiona tutti e sei; scegli un sottoinsieme in /admin → Secure Development → Targets |
| Leggi questo quando… | Documento |
|---|
| Stai installando il kit | docs/hosting.md — prerequisiti, la procedura guidata e ogni singolo passo, come i punteggi raggiungono la macchina, l'app GitHub OAuth, la configurazione dell'evento |
| Stai distribuendo su un cloud | docs/aws.md (Terraform: ECS Fargate + ElastiCache + ALB) · docs/fly.md (una macchina Fly) |
| Stai per aprire le porte | docs/security-checklist.md — la passeggiata pre-evento di una pagina |
| Stai eseguendo l'evento | docs/operations.md — squadre, il pannello di amministrazione, le guide per gli organizzatori di quiz/classic/ai, verifica, teardown |
| Vuoi capire il sistema | docs/architecture.md — diagramma, flusso dei dati dei punteggi, chiavi Redis, modello di sicurezza, strategia di testing |
| Stai scrivendo una rubric | docs/scorer.md — modalità serve + judge, entrambe le grammatiche delle rubric, authoring e build |
| Stai costruendo un nuovo modulo | docs/modules.md — il contratto piattaforma/modulo |
| Ti chiedi "perché è fatto così?" | docs/decisions.md — ADR numerati |