
Selbst gehostetes OWASP-CTF-Kit: eine Box, eine kostenlose GitHub-Organisation, keine Cloud-Abhängigkeiten
Eine selbst gehostete Steuerungsebene für Sicherheits-Lernveranstaltungen — eine Box, eine kostenlose GitHub-Organisation.
Betreibe sie für eine Universität, eine Highschool, ein OWASP-Chapter, ein Meetup.
Lies AGENTS.md, bevor du Code schreibst. Es ist das
Betriebshandbuch: die exakten Befehle, die CI ausführt, die Fehlermodi, die
dieses Repo bereits getroffen hat, und die Review-Invarianten in
docs/reviewing.md. CLAUDE.md ist ein Verweis auf
dieselbe Datei.
Eine Änderung ist bereit, wenn CI grün ist und jeder umsetzbare CodeRabbit-Thread zum neuesten Commit aufgelöst (oder protokolliert abgelehnt) ist. Commits folgen Conventional Commits und tragen keine KI-Zuschreibung.
Kleine, gut spezifizierte Arbeit ist mit
good first issue
getaggt. Neue Module beginnen als Issue, nicht als PR — siehe
CONTRIBUTING.md.
Eine Steuerungsebene, kein einzelnes Spiel. Die Box gibt einer Veranstaltung ihr gemeinsames Rückgrat — eine GitHub-Organisation, Team-Registrierung, ein Live-Leaderboard, ein Organisator-Admin-Panel und die Scoring-Pipeline, die es speist. Module stecken Challenge-Inhalte in dieses Rückgrat, und jede Teilmenge kann allein oder zusammen laufen: Patch-to-Score Secure Development, eine Quiz-Bank, ein Jeopardy-Board und extern gehostete AI-Challenges. Der Modulvertrag ist die Grenze zwischen Rückgrat und Inhalt, sodass die Box darauf ausgelegt ist, weitere Module zu hosten — Forensik, API-Sicherheit, Cloud — sobald sie landen.
Warum es existiert. Das Secure-Development-Modul lehrt Verteidigung statt Angriff, und es ist eine wirklich gute Art, sicheres Programmieren zu lehren. Bisher bedeutete es, Vercel, Upstash, Lambda und DynamoDB aufzusetzen, die Cloud-Rechnung zu tragen und Zugang zu einem privaten Scoring-Image zu haben. Das ist eine zumutbare Anforderung für eine Konferenz mit Budget. Es ist eine unzumutbare Anforderung für einen universitären Sicherheitskurs, einen Highschool-Club, einen OWASP-Chapter-Abend oder einen Wochenend-Workshop.
Dieses Kit beseitigt das. Alles läuft aus Docker Compose auf einer Maschine, die du bereits hast — ein Laptop, ein Ersatz-Desktop, ein kleiner VPS — plus eine kostenlose GitHub-Organisation für die Forks. Die Bewertungsraster für alle sechs Ziele werden im Kit mitgeliefert, sodass es kein privates Image anzufordern und keinen Scoring-Code zu schreiben gibt. Nichts wird abgerechnet, nichts telefoniert nach Hause, und wenn die Veranstaltung endet, archivierst du die Repos und stoppst den Stack.
Für wen es ist: jeden, der diese Veranstaltung durchführen möchte und dafür nicht zum Cloud-Betreiber werden will — Kursleitende, Club-Organisatoren, OWASP-Chapter-Leads, Workshop-Moderatoren, Sicherheitsteams, die einen internen Trainingstag veranstalten.
Bereitgestellt und Ende-zu-Ende durchgespielt; noch nicht für eine echte
Kohorte betrieben. Der vollständige Scoring-Pfad wird im Kit mitgeliefert —
der bearer-authentifizierte POST /score des Scorers, der eigenständige
Scoring-Workflow für die Forks, der Poll-Transport — und scripts/smoke.sh
treibt diese gesamte Pipeline gegen Mocks an. Darüber hinaus läuft das Kit
kontinuierlich auf einer gehosteten Box aus derselben Compose-Datei, die
dieses Repo mitliefert, GET /health meldet die exakte Revision, die es
bedient, und ein Ende-zu-Ende-Durchlauf über diese Live-Instanz ist der Ort,
an dem eine Reihe echter Defekte gefunden und behoben wurde — von der Art,
die eine gemockte Testsuite nicht sehen kann.
Was nicht passiert ist, ist eine echte Veranstaltung: eine Kohorte von Teilnehmenden, die gleichzeitig über Stunden echte PRs gegen echte Forks öffnen. Das ist die Lücke zwischen „die Pipeline funktioniert" und „die Pipeline funktioniert bei 40 Personen". Zwei Einschränkungen sind offen statt vergraben: Der Security-Shepherd-Ergebnisabgleich hat eine angegebene Restgrenze (eine ungewöhnlich formulierte Ablehnung kann immer noch als Lösung gelesen werden — sie kann einen korrekten Patch unterbewerten, aber niemals einen Gratispunkt vergeben), und das Lastprofil einer vollen Kohorte ist ungetestet. Details und aktueller Stand: Status und Upstream-Abhängigkeiten.
Was es tut, was diese nicht tun: Patch-to-Score-Verteidigungstraining, bewertet über GitHub-Pull-Requests, ein Modulvertrag zum Mischen von Spieltypen auf einem Leaderboard und eine Steuerungsebene, die dir vollständig gehört — eine Box, eine kostenlose Organisation, keine Cloud-Rechnung, keine Telemetrie.
Dieses Projekt ist nicht mit der OWASP Foundation verbunden und wird von ihr nicht unterstützt. Vier der sechs verwundbaren Ziele sind OWASP-Projekte (Juice Shop, WebGoat, Security Shepherd, VulnerableApp); DVWA und VAmPI sind Community-Projekte.
Sieh es in zwei Minuten laufen — keine GitHub-Organisation, keine
OAuth-App, nichts zu konfigurieren. Du brauchst Docker mit Compose v2 und
openssl:```sh
git clone https://github.com/OWASP/owasp-ctf-in-a-box
cd owasp-ctf-in-a-box
./scripts/dev-stack up
Es schreibt wegwerfbare lokale Secrets, baut die Scorer- und App-Images, bringt
den Stack hoch, befüllt eine Demo-Bestenliste über die echte Scoring-API des Scorers
und gibt die URL zum Öffnen aus. Du solltest die Bestenliste mit geseedeten Teams
und einen Punktestand-über-Zeit-Graphen sehen; `./scripts/dev-stack score <login> juice-shop 3`
landet drei weitere Solves live. `./scripts/dev-stack down` baut es wieder ab.
**Führe ein echtes Event durch** mit dem geführten Assistenten. Füge die **[`gh`
CLI](https://cli.github.com)** hinzu (authentifiziert), plus **eine kostenlose GitHub-Org**,
falls das Event Secure Development ausführt; `./setup/ctf-setup.sh check` verifiziert
zuerst die Tooling:```sh
./setup/ctf-setup.sh # guided, prompts for values, resumable
Es fragt jeden Wert im Verlauf ab — deine Box-URL, die Event-Org, die Admin-Logins, ob du Secure Development betreibst, die GitHub-Zugangsdaten — schreibt .env, erledigt jeden automatisierbaren Schritt, führt dich durch die GitHub-UI-Schritte und setzt fort, wenn du aufhörst und zurückkommst. Alles andere (der Name des Events, welche Module laufen, welche Targets) ist eine Laufzeit-Einstellung unter /admin, es gibt also keine Konfigurationsdatei zu bearbeiten. Es fragt nur, was du tatsächlich brauchst: ein Event ohne Secure Development braucht keine Org, keine Forks und kein Scorer-Image und wird nie danach gefragt. Zeige jeden mutierenden Schritt mit --dry-run als Vorschau an — er erzählt die Schritte 4–9 aus einer bereits vollständigen .env und verweigert (by design) die Ausführung, wenn kein Admin-Login vorhanden ist oder wenn Secure Development aktiviert ist, aber keine Org existiert. Der Wizard schließt ab, indem er ./setup/ctf-setup.sh doctor ausführt — eine Statusmatrix pro Fork, die du jederzeit erneut ausführen kannst — und bietet dann ein optionales fly.io-Deploy an (Standard: nein), sodass das Bereitstellen desselben Events unter einem öffentlichen Hostnamen ein geführter Ablauf ist — der Hostname, ein in der Vorschau angezeigtes Deploy, dann eine Bestätigung — statt einer Reise durch die Deploy-Dokumentation.
Willst du die Details? Jedes einzelne Subkommando, jeder reine UI-Schritt und wie sich die beiden GitHub-Apps unterscheiden:
docs/hosting.md.
Lieber in einer Cloud? docs/aws.md (Terraform: ECS Fargate, ElastiCache und ein ALB — apply hoch / destroy runter) oder
docs/fly.md (eine Fly-Maschine).
Secure Development — forke eine absichtlich verwundbare App, finde den Fehler, patche ihn, öffne einen PR. Eine GitHub Action im Fork führt die Rubric des Targets gegen den Patch aus, und der Score landet auf dem Leaderboard (~30 s später im Poll-Modus). Sechs Targets, 321 Challenges; der Ausgangszustand ergibt 0 Punkte, ein korrekter Patch verdient seine Punkte — in beide Richtungen abgesichert. Benötigt die GitHub-Org und die Scoring-Pipeline.
Quiz — Single- und Multi-Select-Sicherheitsfragen, die in der App bewertet werden, sobald sie beantwortet sind (bei Multi-Select alles-oder-nichts), mit Versuchslimit und Retry-Cooldown. Werden über /admin einzeln erstellt oder als ein JSON-Bundle importiert und exportiert. Benötigt kein GitHub, keine Forks, keine Pipeline.
Jeopardy — ein Board aus von Organisatoren erstellten Flags in Kategorien. Einsendungen werden getrimmt und normalisiert, Groß-/Kleinschreibung wird verziehen, es sei denn, ein Flag ist als case-sensitive markiert (die Karte sagt es), mit Einsende-Cooldown und optionalen bezahlten Hinweisen. Dieselbe /admin- + JSON-Bundle-Erstellung wie beim Quiz. Benötigt ebenfalls kein GitHub.
AI — Prompt-Injection- und Guardrail-Challenges, die außerhalb der Box gehostet werden. Die Challenge-Seite jedes Teilnehmers erzeugt einen persönlichen Startlink zur externen Seite; eine Lösung meldet sich zurück ans Leaderboard, entweder über den eigenen Callback dieser Seite oder ein Flag, das in die App zurückgetippt wird. Benötigt kein GitHub, keine Forks, keine Pipeline.
Rund um die Module, die du aktivierst, bietet die Plattform: Team-Selbstregistrierung mit Captains, Join-Codes und /join/<code>-Links (Solo-Spiel ist ein Team aus einer Person; ein Flag, das von mehreren Teammitgliedern gelöst wurde, zählt einmal); das Live-Leaderboard mit einem CTFd-artigen Score-über-Zeit-Diagramm aus echten Zeitstempeln pro Lösung; das allowlistete /admin-Panel — Freeze, Scoring- und Registrierungsfenster, Hinweise und Kosten, Team-Obergrenze, Cooldowns, Modulinhalte, Support-Aktionen pro Teilnehmer, ein Aktivitätsstream und Engagement-Metriken — alles zur Laufzeit, kein Rebuild; und ein begrenztes Audit-Log bei jeder Admin-Aktion.
| Teilnehmer-Aufschlüsselung | Challenge-Browser |
|---|---|
![]() | ![]() |
| Jeopardy-Flag-Board | Quiz |
|---|---|
![]() | ![]() |
Aufgenommen aus der Teilnehmer-App, die lokal über scripts/dev-stack up mit vorbelegten Demo-Spielern läuft. Targets und Fork-Links werden durch die Event-Konfiguration gesteuert; der Event-Name und der Rest seines Brandings sind Einstellungen im Admin-Panel.
Ein Docker-Compose-Stack: Caddy terminiert TLS vor der Next.js-App; die App spricht mit Redis nur über srh (ein Upstash-kompatibler REST-Proxy) — das Netzwerk ist aufgeteilt, sodass nichts, was dem Internet ausgesetzt ist, eine Route zu redis:6379 hat. Quiz, Jeopardy und AI bewerten innerhalb der App und schreiben Punkte direkt nach Redis. Secure Development wird außerhalb der Box bewertet: Der Fork des Teilnehmers führt eine GitHub Action aus, die das Target startet, die Rubric gegen den Patch laufen lässt und einen maschinenlesbaren Score-Kommentar am PR hinterlässt. Der sync-Poller zieht diese Kommentare — null eingehende Netzwerkoberfläche, sodass die Box hinter NAT und in Veranstaltungs-WLAN funktioniert (das ist der einzige Transportweg: Push-Ingest wurde in v0.6 entfernt, siehe #377). Der Score gelangt über einen einzigen auditierten Writer hinein:
das bearer-authentifizierte POST /score des Scorers, das validiert und monoton schreibt — Lösungen werden nie durch einen später fehlschlagenden Lauf un-gelöst.
Das vollständige Bild — Komponenten, der neunstufige Score-Datenfluss, das Sicherheitsmodell — steht in docs/architecture.md.
Der Inhalt dieses Moduls ist eine Reihe verwundbarer Targets und ihrer Scoring-Rubrics. Teilnehmer wählen ein Target, forken die Kopie der Org, patchen sie und öffnen einen PR. Die Challenges jedes Targets sind ausführbare node:test-Suiten, bepreist nach Schwierigkeit.
Die Zählungen werden von Hand gepflegt und durch
apps/web/src/lib/tests/apps-catalogue.test.ts an die vendored Rubric gebunden — überprüfe sie nach einem vendor-rubric.sh-Bump erneut. Referenz-Patches, die belegen, dass ein korrekter Fix punktet (das Gate in positiver Richtung), liegen separat unter patches/.
Die Rubrics liegen in scorer/rubric.owasp/, vendored von
OWASP-CTF/dc34-owasp-secure-development-ctf
und an den einzelnen Upstream-Commit gebunden, der in
scorer/rubric.owasp/PROVENANCE.md festgehalten ist. Re-vendor gegen einen neueren Commit mit:```sh
./scripts/vendor-rubric.sh --all --ref
Zwei Rubrik-Formen werden gleichzeitig unterstützt, und ein einzelnes Rubrik-Verzeichnis kann sie mischen: `<target>.yaml`-Dateien verwenden die deklarative HTTP-Request/Expect-Probe-Grammatik, und `<target>/tests/challenges/`-Verzeichnisse verwenden ausführbare Tests, die über `catalogue.<target>.json` bepreist werden. Autorenanleitung:
[docs/scorer.md](https://github.com/owasp/owasp-ctf-in-a-box/blob/main/docs/scorer.md).
**Zur Rubrik-Geheimhaltung.** Diese Rubriken sind öffentlich. Die Targets sind Open Source und ihre Lösungen bereits veröffentlicht, daher behandelt das Kit die Rubrik-Privatsphäre als Schutz gegen Check-Gaming statt gegen das Kennen der Antworten — ein akzeptierter Kompromiss für ein selbst gehostetes Event. Überschreibe jederzeit mit deiner eigenen privaten Rubrik:```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/ ist gitignored und genau dafür reserviert.
Sobald der Stack unter deiner EVENT_URL läuft:
/admin: das Leaderboard einfrieren, die Registrierung
öffnen und schließen, den Zeitplan festlegen, Quizfragen, Classic-Challenges
und ai-Challenges verfassen — und wenn ein Teilnehmender feststeckt, genau diesen einen
Teilnehmenden reparieren, statt die Veranstaltung zurückzusetzen.docker compose logs -f sync (er läuft mit
aktiviertem secure-development). Der gesamte Zustand liegt in benannten Docker-Volumes, sodass
ein Neustart der Box nichts verliert../setup/ctf-setup.sh teardown die Ziel-
Repos — dann deinstalliere die GitHub App und lösche die Actions-Secrets der Org
selbst. Eine Veranstaltung ohne secure-development hat keine Forks zu archivieren.Teams, das Admin-Panel, das Verifizieren des Kits vor dem großen Tag und der lokale Dev-Stack sind alle in docs/operations.md behandelt; Voraussetzungen, der Score-Transport, OAuth-Einrichtung und Event-Konfiguration in docs/hosting.md.
Die vollständige Begründung, Alternativen und Abwägungen sind als nummerierte ADRs in docs/decisions.md festgehalten.
Gerendert unter owasp.github.io/owasp-ctf-in-a-box.
Beiträge willkommen — CONTRIBUTING.md behandelt die Dev- Umgebung, die CI-Gates und wie man ein Modul vorschlägt; CODE_OF_CONDUCT.md gilt.
Agents sollten AGENTS.md befolgen. Die folgenden Befehle entsprechen der CI;
make help listet dieselben Targets auf.
Jeder Service testet unabhängig (durchgängig 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
Eine Schwachstelle im Kit selbst gefunden? **[SECURITY.md](https://github.com/owasp/owasp-ctf-in-a-box/blob/main/SECURITY.md)** — die
Schwachstellen der Targets sind beabsichtigt und außerhalb des Geltungsbereichs.
## Lizenz und Credits
MIT — siehe [LICENSE](https://github.com/owasp/owasp-ctf-in-a-box/blob/main/LICENSE). Der Rubrik-Inhalt unter `scorer/rubric.owasp/`
ist aus dem Upstream-Event
[OWASP-CTF](https://github.com/OWASP-CTF/dc34-owasp-secure-development-ctf)
übernommen und auf den Commit in `scorer/rubric.owasp/PROVENANCE.md` festgelegt — dieses Kit
existiert, weil dieses Event es wert war, mehr als einmal durchgeführt zu werden. Die verwundbaren
Targets sind nicht übernommen: Events forken sie von ihren eigenen 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)), und jedes behält seine eigene Lizenz.
OWASP® ist eine eingetragene Marke der OWASP Foundation; dieses Projekt ist nicht
mit ihr verbunden oder von ihr unterstützt.
| Target | Challenges | Punkte | Hinweise |
|---|
vulnerableapp | 110 | 187 | Größtes Target; wird 8-fach parallel bewertet |
webgoat | 69 | 137 | Zweistufiger Build: Maven, dann das runtime-only Dockerfile des Forks |
dvwa | 55 | 108 | Benötigt ein MariaDB-Sibling und eine Schema-Initialisierung |
securityshepherd | 40 | 79 | HTTPS, Drei-Container-Stack, strikt seriell |
juice-shop | 38 | 141 | Das einzige Target, dessen Schwierigkeit bis 6 Sterne reicht |
vampi | 9 | 16 | Eigenständig; der schnellste End-to-End-Nachweis |
| Gesamt | 321 | 668 | Jedes Event provisioniert alle sechs; wähle eine Teilmenge in /admin → Secure Development → Targets |
| Lies dies, wenn du… | Dokument |
|---|
| das Kit aufsetzt | docs/hosting.md — Voraussetzungen, der Wizard und jeder einzelne Schritt, wie Scores die Box erreichen, die GitHub OAuth App, Event-Konfiguration |
| in eine Cloud deployst | docs/aws.md (Terraform: ECS Fargate + ElastiCache + ALB) · docs/fly.md (eine Fly-Maschine) |
| kurz davor bist, die Türen zu öffnen | docs/security-checklist.md — der einseitige Pre-Event-Durchgang |
| die Veranstaltung durchführst | docs/operations.md — Teams, das Admin-Panel, die Quiz-/Classic-/ai-Organizer-Guides, Verifizieren, Teardown |
| das System verstehen willst | docs/architecture.md — Diagramm, Score-Datenfluss, Redis-Keys, Sicherheitsmodell, Teststrategie |
| eine Rubric schreibst | docs/scorer.md — Serve- + Judge-Modi, beide Rubric-Grammatiken, Verfassen und Bauen |
| ein neues Modul baust | docs/modules.md — der Plattform-/Modul-Vertrag |
| dich fragst „warum ist es so?" | docs/decisions.md — nummerierte ADRs |