一个用于安全学习活动的自托管控制平面——一台机器,一个免费的 GitHub 组织。
可为大学、高中、OWASP 分会或聚会运行。
在编写代码之前,请先阅读 AGENTS.md。它是操作手册:
CI 运行的确切命令、本仓库已经遇到过的失败模式,以及
docs/reviewing.md 中的审查不变量。CLAUDE.md 是指向
同一文件的指针。
当 CI 通过 且 最新提交上每个可操作的 CodeRabbit 讨论串都已解决 (或有记录地拒绝)时,变更才算就绪。 提交遵循 Conventional Commits,且不包含 AI 署名。
小而明确的工作会被标记为
good first issue。
新模块从 issue 开始,而不是 PR——参见
CONTRIBUTING.md。
一个控制平面,而非单一游戏。 这台机器为活动提供共享 主干——一个 GitHub 组织、队伍注册、实时排行榜、组织者 管理面板,以及为其提供数据的评分流水线。模块 将 挑战内容接入该主干,任意子集都可以单独或一起运行: 按补丁评分的 Secure Development、Quiz 题库、 Jeopardy 面板,以及外部托管的 AI 挑战。模块 契约 是主干与内容之间的边界, 因此这台机器被构建为可承载后续落地的更多模块—— 取证、API 安全、云。
它为何存在。 Secure Development 模块教授的是防御而非 攻击,而且它确实是一种教授安全编码的好方法。在此之前, 运行一次意味着要搭建 Vercel、Upstash、Lambda 和 DynamoDB,承担 云账单,并拥有一个私有评分镜像的访问权限。对于有预算的会议来说, 这是合理的要求。对于大学安全课程、高中社团、OWASP 分会之夜或 周末工作坊来说,这是不合理的要求。
本套件消除了这些要求。一切都在你已有的单台机器上通过 Docker Compose 运行——一台笔记本电脑、一台闲置台式机、一台小型 VPS——再加上一个免费的 GitHub 组织用于 fork。全部六个目标的评分标准都随套件提供,因此 无需申请私有镜像,也无需编写评分代码。没有任何计费,没有任何数据外传, 活动结束后你归档仓库并停止该技术栈。
适用对象: 任何想运行此活动、又不想为此成为云运维人员的人—— 课程讲师、社团组织者、OWASP 分会负责人、工作坊主持人、举办内部 培训日的安全团队。
已部署并端到端演练;尚未面向真实群体运行。 完整的
评分路径随套件提供——评分器经 bearer 认证的 POST /score、
用于 fork 的自包含评分工作流、轮询传输——并且
scripts/smoke.sh 针对 mock 驱动整条流水线。除此之外,
本套件使用本仓库提供的同一份 Compose 文件在一台托管机器上持续运行,
GET /health 会报告正在为其提供服务的准确修订版本,而
对那个实时实例进行的一次端到端测试正是发现并修复一批真实缺陷的地方——
这类缺陷是 mock 测试套件无法看到的。
尚未发生的是真实活动:一群参赛者同时针对真实 fork 打开真实 PR,持续数小时。这就是“流水线能工作”与“流水线能在 40 人规模下工作”之间的 差距。有两个注意事项是公开的而非被掩盖的:Security Shepherd 结果匹配器有一个已说明的 残余限制(措辞异常的拒绝仍可能被读作已解——它可能对正确补丁少给分,但绝不会 白送分),以及完整群体的负载情况尚未测试。详情与当前状态:状态与 上游依赖。
它做到了那些平台做不到的事:通过 GitHub pull request 评分的按补丁评分防御训练、用于在同一 排行榜上混合多种游戏类型的模块契约,以及一个你端到端拥有的控制平面——一台机器、一个免费组织、 无云账单、无遥测。
本项目不隶属于 OWASP Foundation,也未获其认可。 六个易受攻击目标中有四个是 OWASP 项目(Juice Shop、WebGoat、 Security Shepherd、VulnerableApp);DVWA 和 VAmPI 是社区项目。
两分钟内看到它运行起来——无需 GitHub 组织、无需 OAuth 应用、无需
配置任何东西。你需要 带 Compose v2 的 Docker 和 openssl:```sh
git clone https://github.com/OWASP/owasp-ctf-in-a-box
cd owasp-ctf-in-a-box
./scripts/dev-stack up
它写入一次性本地密钥,构建评分器和应用镜像,启动整个栈,通过评分器真实的评分 API 播种一个演示排行榜,并打印要打开的 URL。你应该会看到带有已播种队伍的排行榜和一张分数随时间变化的图表;`./scripts/dev-stack score <login> juice-shop 3` 会实时再计入三次解题。`./scripts/dev-stack down` 将其拆除。
**运行真实赛事**时使用引导式向导。添加 **[`gh`
CLI](https://cli.github.com)**(已认证),如果赛事运行 Secure Development,还需添加**一个免费的 GitHub 组织**;`./setup/ctf-setup.sh check` 会先验证工具链:```sh
./setup/ctf-setup.sh # guided, prompts for values, resumable
它会按顺序询问每个值——你的 box URL、活动组织、管理员登录信息、你是否运行 Secure Development、GitHub 凭据——写入 .env,执行所有可自动化的步骤,引导你完成需要在 GitHub UI 中操作的步骤,并且在你中断后回来时继续。其他所有内容(活动名称、运行哪些模块、哪些目标)都是运行时的 /admin 设置,因此没有需要编辑的配置文件。它只询问你实际需要的内容:没有 Secure Development 的活动不需要组织、不需要 fork、也不需要 scorer 镜像,并且永远不会被问到这些。用 --dry-run 预览任何变更步骤——它会根据已经完整的 .env 叙述步骤 4–9,并在没有管理员登录信息,或 Secure Development 已开启但没有组织时(按设计)拒绝执行。向导最后会运行 ./setup/ctf-setup.sh doctor——一个你可以随时重新运行的按 fork 状态矩阵——然后提供可选的 fly.io 部署(默认否),因此把同一个活动放到公共主机名上是一个引导式流程——主机名、预览部署,然后确认——而不是翻阅部署文档。
想要细节? 每个离散子命令、每个仅 UI 步骤,以及两个 GitHub 应用有何不同:
docs/hosting.md。
改用云平台? docs/aws.md(Terraform:ECS Fargate、ElastiCache 和一个 ALB——apply 启动 / destroy 关闭)或
docs/fly.md(一台 Fly 机器)。
Secure Development —— fork 一个故意存在漏洞的应用,找到缺陷,修补它,打开一个 PR。fork 中的 GitHub Action 会针对补丁运行目标的评分标准,分数会落到排行榜上(轮询模式下约 30 秒后)。六个目标,321 个挑战;原始状态得 0 分,正确的补丁获得其分值——双向门控。需要 GitHub 组织和评分流水线。
Quiz —— 单选和多选安全题目,在作答的那一刻就在应用内评分(多选为全有或全无),带有尝试次数上限和重试冷却时间。可从 /admin 逐题编写,或作为一个 JSON 包导入和导出。不需要 GitHub、不需要 fork、不需要流水线。
Jeopardy —— 一个由组织者编写的按类别组织的 flag 面板。提交内容会被修剪和规范化,除非某个 flag 被标记为区分大小写(其卡片会注明),否则大小写不敏感,并带有提交冷却时间和可选的付费提示。与 quiz 相同的 /admin + JSON 包编写方式。同样不需要 GitHub。
AI —— 托管在 box 外部的提示注入和护栏挑战。每位参赛者的挑战页面会为他们生成一个指向外部站点的个人启动链接;解出后会回报到排行榜,要么通过该站点自己的回调,要么通过把 flag 输入回应用。不需要 GitHub、不需要 fork、不需要流水线。
围绕你启用的任何模块,平台提供:带队长的团队自助注册、加入码和 /join/<code> 链接(单人游玩就是一个人的团队;由多名队友解出的 flag 只计一次);带有 CTFd 风格分数随时间变化图表的实时排行榜,基于真实的每次解题时间戳;允许列表控制的 /admin 面板——冻结、评分和注册时间窗口、提示和费用、团队上限、冷却时间、模块内容、按参赛者的支持操作、活动流和参与度指标——全部运行时生效,无需重建;以及对每个管理员操作的有上限审计日志。
| 参赛者明细 | 挑战浏览器 |
|---|---|
![]() | ![]() |
| Jeopardy flag 面板 | 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 在 Next.js 应用前面终止 TLS;应用仅通过 srh(一个兼容 Upstash 的 REST 代理)与 Redis 通信——网络被拆分,因此任何面向互联网的组件都没有通往 redis:6379 的路由。Quiz、Jeopardy 和 AI 在应用内评分,并直接把分数存入 Redis。Secure Development 在 box 外部评分:参赛者的 fork 运行一个 GitHub Action,该 Action 启动目标,针对补丁运行评分标准,并在 PR 上发布一条机器可读的分数评论。sync 轮询器拉取这些评论——零入站网络暴露面,因此 box 可在 NAT 后和场地 wifi 上工作(这是唯一的传输方式:推送摄取已在 v0.6 中移除,见 #377)。分数通过单一审计写入器进入:
scorer 的带 bearer 认证的 POST /score,它会验证并以单调方式写入——解题永远不会因后续失败的运行而被取消解题。
完整图景——组件、九步分数数据流、安全模型——见 docs/architecture.md。
该模块的内容是一组存在漏洞的目标及其评分标准。参赛者选择一个目标,fork 组织的副本,修补它,然后打开一个 PR。每个目标的挑战都是可执行的 node:test 测试套件,按难度定价。
计数为手工维护,并由
apps/web/src/lib/tests/apps-catalogue.test.ts 固定到 vendored 评分标准——在 vendor-rubric.sh 升级后请重新核对它们。证明正确修复能得分的参考 补丁(正向门控)单独存放在 patches/ 下。
评分标准位于 scorer/rubric.owasp/,vendored 自
OWASP-CTF/dc34-owasp-secure-development-ctf
并固定到记录在
scorer/rubric.owasp/PROVENANCE.md 中的单个上游提交。使用以下命令针对更新的提交重新 vendor:```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-in-a-box/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:冻结排行榜、开启和关闭注册、设置日程、编写测验题目、经典挑战和 ai 挑战——当某位参赛者卡住时,修复那一位参赛者,而不是重置整个活动。docker compose logs -f sync(它以启用 secure-development 的方式运行)。所有状态都保存在命名的 Docker 卷中,因此机器重启不会丢失任何东西。./setup/ctf-setup.sh teardown 会归档目标仓库——然后你自己卸载 GitHub App 并删除组织的 Actions secrets。没有 secure-development 的活动没有需要归档的 fork。团队、管理面板、活动前验证套件以及本地开发栈都在 docs/operations.md 中涵盖;先决条件、分数传输、OAuth 设置和活动配置在 docs/hosting.md 中。
完整的推理、替代方案和权衡记录在 docs/decisions.md 中,以编号 ADR 形式呈现。
渲染于 owasp.github.io/owasp-ctf-in-a-box。
欢迎贡献——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-in-a-box/blob/main/SECURITY.md)** —— 靶标的漏洞是有意为之,不在范围内。
## 许可证与致谢
MIT —— 参见 [LICENSE](https://github.com/owasp/owasp-ctf-in-a-box/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,然后是 fork 的仅运行时 Dockerfile |
dvwa | 55 | 108 | 需要一个 MariaDB 伴随容器和一次 schema 初始化 |
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 — 团队、管理面板、测验/经典/ai 组织者指南、验证、拆除 |
| 理解系统 | docs/architecture.md — 图表、分数数据流、Redis 键、安全模型、测试策略 |
| 编写评分标准 | docs/scorer.md — serve + judge 模式、两种评分标准语法、编写与构建 |
| 构建新模块 | docs/modules.md — 平台/模块契约 |
| 问“为什么是这样?” | docs/decisions.md — 编号 ADR |