ctfpilot/challenge-toolkit

GitHub: ctfpilot/challenge-toolkit

CTF Pilot 挑战赛工具包是一个用于 CTF 题目开发、部署和管理的综合命令行工具,覆盖从题目初始化到 Kubernetes 部署的完整生命周期。

Stars: 0 | Forks: 2

# CTF Pilot 挑战赛工具包 一个用于 CTF 挑战赛开发、部署和管理的综合 CLI 工具包。 该挑战赛工具包简化了整个 CTF 挑战赛的生命周期,从使用正确的目录结构初始化新挑战,到构建 Docker 镜像和生成 Kubernetes 部署清单。它与 [CTF Pilot 的基础设施](https://github.com/ctfpilot) 无缝协作,强制执行标准化 schema 并自动执行重复性任务,让你可以专注于创建优秀的挑战,而不是管理样板代码。 ## 支持的 CTF Pilot 版本 | CTF Pilot 组件 | 支持版本 | | ---------------------------------------------------------------------------- | ----------------- | | [CTF Pilot 的 CTF 平台 (CTFp)](https://github.com/ctfpilot/ctfp) | v1.0 | | [CTF Pilot 的挑战赛 Schema](https://github.com/ctfpilot/challenge-schema) | v1.0 | | [CTF Pilot 的页面 Schema](https://github.com/ctfpilot/page-schema) | v1.0 | | [CTF Pilot 的 CTFd 管理器](https://github.com/ctfpilot/ctfd-manager) | v1.0 | | [kube-ctf](https://github.com/ctfpilot/kube-ctf) | v1.0 | ## 运行方式 本项目使用标准的 Python 打包方式,因此几乎可以通过任何 Python 包管理器进行安装。 ``` uv tool install challenge-toolkit # 或使用 pipx pipx install challenge-toolkit # 或使用 pip(确保使用虚拟环境) pip install challenge-toolkit ``` 然后你可以从命令行运行该工具: ``` challenge-toolkit [arguments] [options] ``` 为了使用 `create`、`template` 和 `page` 命令,你需要将部署模板复制到你的挑战仓库的 `template/` 目录中(需遵循 **[模板结构](#template-structure)** 章节)。 示例模板可以在 [`template` 目录](./template) 中找到。 ### 环境变量 该工具包支持以下可选的环境变量: | 变量 | 描述 | 使用者 | | ------------------- | ---------------------------------------------------------------------- | ------------------ | | `GITHUB_REPOSITORY` | 格式为 `owner/repo` 的 GitHub 仓库(例如 `ctfpilot/challenges`) | `template`, `page` | ### 依赖项 目前,需要以下依赖项: - Python 3.10 或更高版本 - `pyyaml` Python 包 - `python-slugify` Python 包 - Docker(用于通过 `pipeline` 命令构建挑战镜像) Python 依赖项列在 `pyproject.toml` 文件中,并在通过 pip 或类似工具安装该包时自动安装。 ### 典型用法 该工具通常用于三种场景: 1. **创建新挑战**,使用 `create` 命令。 `slugify` 命令可用于根据名称为挑战创建 slug。 2. **为挑战构建资源**。这包括: 1. **构建 Docker 镜像**,使用 `pipeline` 命令。 2. **渲染 Kubernetes 部署文件**,使用 `template` 命令,对于每种渲染类型,按 `clean`、`k8s`、`configmap`、`handout` 的顺序进行。 3. **渲染 CTFd 页面**,使用 `page` 命令。 ### 配置 可以通过配置 `src/challenge_toolkit/library/config.py` 文件来配置该工具包。 如果你有自定义的挑战 schema 或页面 schema,这很重要。 默认值: ``` # Challenge repository 根目录的路径 CHALLENGE_REPO_ROOT = Path.cwd() # Default to the directory where the command is run from # Challenge 和 Page schema URLs CHALLENGE_SCHEMA = "https://raw.githubusercontent.com/ctfpilot/challenge-schema/refs/heads/main/schema.json" PAGE_SCHEMA = "https://raw.githubusercontent.com/ctfpilot/page-schema/refs/heads/main/schema.json" # schema fields 的允许值 CHALL_TYPES = [ "static", "shared", "instanced" ] DIFFICULTIES = [ "beginner", "easy", "easy-medium", "medium", "medium-hard", "hard", "very-hard", "insane"] CATEGORIES = [ "web", "forensics", "rev", "crypto", "pwn", "boot2root", "osint", "misc", "blockchain", "mobile", "test" ] INSTANCED_TYPES = [ "none", "web", "tcp" ] # "none" is the default. Defines how users interact with the challenge. # 用于 tag 和 flag 验证的 Regex patterns TAG_FORMAT = "^[a-zA-Z0-9-_:;? ]+$" FLAG_FORMAT = "^(\\w{2,10}\\{[^}]*\\}|dynamic|null)$" # 默认 challenge 配置值 DEFAULT = { "enabled": False, "name": None, "slug": None, "author": None, "category": None, "difficulty": None, "type": None, "tags": [], "instanced_name": None, "instanced_type": "none", "instanced_subdomains": [], "connection": None, "flag": {"flag": "null", "case_sensitive": False}, "points": 1000, "decay": 75, "min_points": 100, "description_location": "description.md", "handout_dir": "handout" } ``` ## 命令 该工具包提供了多个命令来管理整个生命周期中的 CTF 挑战。所有命令都遵循以下格式: ``` challenge-toolkit [arguments] [options] ``` ### 命令概览 | 命令 | 用途 | 关键参数 | | ---------- | ------------------------------------------- | -------------------------------------------- | | `create` | 初始化新挑战 | 名称、类别、难度等选项 | | `template` | 生成 K8s 文件、ConfigMap 或分发包 | `` `` | | `pipeline` | 构建并标记 Docker 镜像 | `` `` `` | | `page` | 为 CTFd 页面生成 ConfigMap | `` | | `slugify` | 将字符串转换为 URL 安全的 slug | `` | ### `create` - 创建新挑战 使用正确的目录结构和模板文件初始化新挑战。 **用法:** ``` challenge-toolkit create [options] ``` **选项:** | 选项 | 描述 | 默认值 | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- | | `--no-prompts` | 跳过交互式提示并使用默认/提供的值 | 交互模式 | | `--name ` | 挑战名称 | 提示输入 | | `--slug ` | 挑战的 URL 安全标识符 | 提示输入 | | `--author ` | 挑战作者名称 | 提示输入 | | `--category ` | 挑战类别 | 提示输入 | | `--difficulty ` | 挑战难度 | 提示输入 | | `--type ` | 挑战类型:`static`、`shared` 或 `instanced` | 提示输入 | | `--instanced-type ` | 对于 instanced 挑战:`none`、`web` 或 `tcp`。如果为非 `static` 挑战提供了 `web` 或 `tcp`,则会生成部署模板。 | `none` | | `--flag ` | 挑战 flag(格式:`FLAG{...}` 或 `dynamic` 或 `null`) | 提示输入 | | `--points ` | 挑战的初始积分 | `1000` | | `--min-points ` | 最低积分(用于动态计分) | `100` | | `--description-location ` | 挑战描述文件的路径 | `description.md` | | `--dockerfile-location ` | Dockerfile 的路径(相对于挑战目录) | `src/Dockerfile` | | `--dockerfile-context ` | Docker 构建上下文路径 | `src/` | | `--dockerfile-identifier ` | 多个 Dockerfile 的标识符 | `None` | | `--handout_location ` | 包含要分发给参赛者的文件的目录 | `handout` | **示例:** ``` # 交互模式(推荐首次使用) challenge-toolkit create # 带所有参数的非交互模式 challenge-toolkit create \ --no-prompts \ --name "SQL Injection 101" \ --slug "sql-injection-101" \ --author "John Doe" \ --category web \ --difficulty easy \ --type instanced \ --instanced-type web \ --flag "FLAG{sql_1nj3ct10n_1s_fun}" \ --points 500 \ --min-points 100 ``` ### `template` - 渲染 Kubernetes 模板 为挑战生成 Kubernetes 部署文件、ConfigMap 或分发文件压缩包。 **用法:** ``` challenge-toolkit template [options] ``` **参数:** | 参数 | 描述 | 是否必需 | | ------------- | ------------------------------------------------------------------------ | -------- | | `` | 渲染类型:`k8s`、`configmap`、`clean` 或 `handout` | 是 | | `` | 格式为 `category/slug` 的挑战路径(例如 `web/sql-injection-101`) | 是 | **选项:** | 选项 | 描述 | 默认值 | | ----------------------- | ------------------------------------------- | -------------------------------------------- | | `--expires ` | 挑战实例到期前的秒数 | `3600`(1 小时) | | `--available ` | 挑战变为可用状态前的秒数 | `0`(立即) | | `--repo ` | 格式为 `owner/repo` 的 GitHub 仓库 | `$GITHUB_REPOSITORY` 环境变量或为空(见注释) | **渲染器类型:** - **`k8s`** - 为挑战生成 Kubernetes 部署 YAML 文件。 如果挑战类型是 `instanced`,它将使用 `template/k8s.yml` 文件作为模板,生成 `k8s/challenge/k8s.yml` 文件。它将挑战模板嵌入到 `kube-ctf` 部署模板中。 如果挑战类型是 `shared` 或 `static`,它将使用 `template/k8s.yml` 文件作为模板,连同位于 `k8s/challenge/` 的完整 helm chart 一起,生成 `k8s/challenge/template/k8s.yml` 文件。 它将渲染以下字段: - `CHALLENGE_NAME` - 挑战 slug - `CHALLENGE_CATEGORY` - 挑战类别 - `CHALLENGE_TYPE` - 挑战类型 - `CHALLENGE_VERSION` - 挑战版本 - `CHALLENGE_EXPIRES` - 到期时间(以秒为单位) - `CHALLENGE_AVAILABLE_AT` - 挑战变为可用的时间 - `DOCKER_IMAGE` - 由类别和 slug 组合而成的 Docker 镜像名。将不会遵循 `pipeline` 命令生成的格式。 模板渲染使用 `{{ VARIABLE_NAME }}` 语法。 - **`configmap`** - 生成包含挑战元数据和描述的 helm chart,它为 [CTF Pilot 的 CTFd 管理器](https://github.com/ctfpilot/ctfd-manager) 生成 ConfigMap。 这将从全局模板目录渲染 `challenge-configmap.yml`,连同位于 `k8s/config/` 的完整 helm chart 一起,生成 `k8s/config/templates/k8s.yml` 文件。 它将渲染以下字段: - `CHALLENGE_NAME` - 挑战 slug - `CHALLENGE_CATEGORY` - 挑战类别 - `CHALLENGE_REPO` - 格式为 `owner/repo` 的 GitHub 仓库,使用 `--repo` 选项或 `GITHUB_REPOSITORY` 环境变量 - `CHALLENGE_PATH` - 格式为 `challenges//` 的挑战路径 - `CHALLENGE_TYPE` - 挑战的 instanced 类型 - `CHALLENGE_VERSION` - 挑战版本 - `CHALLENGE_ENABLED` - 是否启用挑战 - `HOST` - 挑战的主机名。将被替换为 helm 模板变量 `{{ .Values.kubectf.host }}` - `CURRENT_DATE` - 格式为 `%Y-%m-%d %H:%M:%S` 的当前日期 模板渲染使用 `{{ VARIABLE_NAME }}` 语法。 - **`clean`** - 从 `k8s/` 目录中删除所有生成的 Kubernetes 文件 - **`handout`** - 创建分发包目录中文件的 ZIP 压缩包。 创建的压缩包作为 `_.zip` 存储在 `k8s/files/` 目录中。它将忽略 `.gitkeep` 和 `.gitignore` 文件。 **示例:** ``` # 生成 Kubernetes 部署文件 challenge-toolkit template k8s web/sql-injection-101 # 生成具有自定义过期时间(2小时)和 repo 的 ConfigMap challenge-toolkit template configmap web/sql-injection-101 \ --expires 7200 \ --repo ctfpilot/ctf-challenges # 创建 handout archive challenge-toolkit template handout web/sql-injection-101 # 清理生成的文件 challenge-toolkit template clean web/sql-injection-101 ``` ### `pipeline` - 构建并标记 Docker 镜像 为挑战构建 Docker 镜像,并为容器镜像仓库部署进行适当的标记。 **用法:** ``` challenge-toolkit pipeline [options] ``` **参数:** | 参数 | 描述 | 是否必需 | | ---------------- | ----------------------------------------------------------------- | -------- | | `` | 格式为 `category/slug` 的挑战路径(例如 `web/example`) | 是 | | `` | 容器镜像仓库 URL(例如 `ghcr.io`、`docker.io`) | 是 | | `` | Docker 镜像名的前缀,例如仓库名称 | 是 | **选项:** | 选项 | 描述 | 默认值 | | ------------------------- | ------------------------------- | ------- | | `--image_suffix ` | 附加到镜像名称的后缀 | None | **行为:** - 自动增加挑战版本号 - 使用 `challenge.yml` 中指定的 Dockerfile 位置构建 Docker 镜像 - 同时使用 `:latest` 和 `:version` 标签标记镜像 - 镜像命名:`/--[-identifier][-suffix]` **示例:** ``` # 构建并 tag Docker image challenge-toolkit pipeline \ web/sql-injection-101 \ ghcr.io \ ctfpilot/ctf-challenges # 使用自定义后缀构建(例如,用于 staging) challenge-toolkit pipeline \ web/sql-injection-101 \ ghcr.io \ ctfpilot/ctf-challenges \ --image_suffix staging # 结果:ghcr.io/ctfpilot/ctf-challenges-web-sql-injection-101:latest # ghcr.io/ctfpilot/ctf-challenges-web-sql-injection-101:1 ``` ### `page` - 渲染 CTFd 页面 生成 Kubernetes ConfigMap 页面,遵循 [CTF Pilot 的页面 Schema](https://github.com/ctfpilot/page-schema)。 **用法:** ``` challenge-toolkit page [options] ``` **参数:** | 参数 | 描述 | 是否必需 | | -------- | ---------------------------------- | -------- | | `` | 页面路径(例如 `rules`、`about`) | 是 | **选项:** | 选项 | 描述 | 默认值 | | --------------------- | ---------------------------------------- | -------------------------------------------- | | `--repo ` | 格式为 `owner/repo` 的 GitHub 仓库 | `$GITHUB_REPOSITORY` 环境变量或为空(见注释) | **示例:** ``` # 渲染自定义页面 challenge-toolkit page rules --repo ctfpilot/ctf-challenges # 渲染关于页面 challenge-toolkit page about ``` ### `slugify` - 将字符串转换为 URL 安全的 slug 将挑战名称转换为遵循工具包约定的、URL 安全的 slug 的实用命令。 **用法:** ``` challenge-toolkit slugify ``` **参数:** | 参数 | 描述 | 是否必需 | | -------- | ------------------------- | -------- | | `` | 要转换为 slug 的字符串 | 是 | 示例:** ``` # 将 challenge 名称转换为 slug challenge-toolkit slugify "SQL Injection 101" # 输出:sql-injection-101 # 使用特殊字符转换 challenge-toolkit slugify "Web: XSS & CSRF" # 输出:web-xss-csrf ``` ## 挑战仓库结构 工具期望一个特定的目录结构,其中挑战存储在 `challenges` 目录中。 在 `challenges` 目录中,挑战按类别划分。 每个挑战都存储在自己的目录中,其名称与挑战的 slug 相同。 除了 `challenges` 目录外,还有一个 `template` 目录,其中包含 Kubernetes 部署文件的基础模板。 目录结构如下: ``` . ├── challenges/ │ ├── web │ ├── forensics │ ├── rev │ ├── crypto │ ├── pwn │ ├── boot2root │ ├── osint │ ├── misc │ ├── blockchain │ └── beginner/ │ └── challenge-1 ├── pages/ │ └── page-1/ ├── template/ └── ``` *如果需要,可以将 `pages` 拆分到独立的仓库中。* ### 挑战结构 每个挑战都存储在自己的目录中,其名称与挑战 slug 相同。 在挑战目录中,有几个子目录和文件构成了该挑战。 挑战的子目录结构如下: ``` . ├── handout/ ├── k8s/ ├── solution/ ├── src/ ├── template/ ├── challenge.yml ├── description.md ├── README.md └── version ``` - `handout/` 包含要分发给用户的文件。这可能是需要逆向的二进制文件、需要分析的 pcap 文件等。此目录中的文件会自动压缩并作为 `_.zip` 存储在 `k8s/files/` 目录中。 - `k8s/` 包含挑战的 Kubernetes 部署文件。这是自动生成的,用于部署到 CTF 平台。不应手动修改此目录,而是使用 `challenge.yml` 文件来指定部署文件。 - `solution/` 包含用于解答挑战的脚本。由挑战创建者填写。不强制要求内容的进一步规范。 - `src/` 包含挑战的源代码。它包含了运行挑战所需的所有代码。它也可能包含需要分发的任何副本。Dockerfile、Python 脚本等都在这里。 - `template/` 包含挑战的模板文件。例如 Kubernetes 部署文件或类似文件,它们将使用 `challenge.yml` 文件中的数据进行渲染。 - `challenge.yml` 包含挑战的元数据。这必须由挑战创建者填写。遵循非常严格的结构,可以在文件提供的 schema 文件中找到。 该文件可以替换为 JSON 文件,即 `challenge.json`。 - `description.md` 包含挑战的描述。这是用户打开挑战时向他们显示的文本。应该用 Markdown 编写。 - `README.md` 包含挑战的基本思路和信息。可能包含灵感或关于挑战的其他内部说明。也可能包含解题步骤。 - `version` 包含挑战的版本。由 `pipeline` 命令自动更新。包含一个数字,即挑战的版本号。 要了解有关 `challenge.yml` 文件的更多信息,请参阅 [CTF Pilot 的挑战 Schema](https://github.com/ctfpilot/challenge-schema)。 #### 包含 Dockerfile 的挑战 在挑战中使用 Docker 是非常常见的,因为它是 shared 和 instanced 挑战的核心。 Docker 镜像使用 `pipeline` 命令构建。 它们是基于 `challenge.yml` 文件中提供的 Dockerfile 构建的。 每个 Dockerfile 的位置都是相对于单个挑战目录的。 对于 dockerfile,应该在 `challenge.yml` 文件中的 `dockerfile_locations` 键下描述以下内容: - `location`:相对于挑战目录的 Dockerfile 位置。示例:`src/Dockerfile`。 - `context`:相对于挑战目录的 Dockerfile 上下文。示例:`src/`。 上下文控制 Docker 在哪里寻找要包含在构建过程中的文件。 - `identifier`:Dockerfile 的标识符,用于作为 Docker 镜像的后缀。示例:`web`、`db`、`app`、`bot`。 当一个挑战需要多个 Docker 镜像时,会使用此标识符。 *如果只描述了单个 Dockerfile,则可以省略此项。* 此格式遵循 [CTF Pilot 的挑战 Schema](https://github.com/ctfpilot/challenge-schema)。
点击展开示例 这是一个包含多个 Dockerfile(一个用于应用程序,一个用于数据库)的示例: ``` dockerfile_locations: - location: src/app/Dockerfile context: src/app/ identifier: app - location: src/db/Dockerfile context: src/db/ identifier: db ``` 此示例的文件夹结构将是: ``` . └── src/ ├── app/ │ ├── Dockerfile │ └── └── db/ ├── Dockerfile └── ```

Docker 镜像命名规范在上面的 [`pipeline` 命令章节](#pipeline---build-and-tag-docker-images) 中进行了说明。 ### 模板结构 `template/` 目录包含挑战部署文件的基础模板。 这些模板用于在运行 `template` 和 `page` 命令时,在 `k8s/` 目录中生成实际的部署文件。 它们也用于运行 `create` 命令时的初始挑战创建过程。 需要以下模板: - ConfigMap 模板: - `challenge-configmap.yml` - `page-configmap.yml` - 挑战部署模板: - Instanced web:`instanced-web-k8s.yml` - Instanced TCP:`instanced-tcp-k8s.yml` - Shared Web:`shared-web-k8s.yml` - Shared TCP:`shared-tcp-k8s.yml` - [kube-ctf](https://github.com/ctfpilot/kube-ctf) 部署模板: - `instanced-k8s-challenge.yml` **Configmap 模板**用于为挑战和页面生成 ConfigMap。 **挑战部署模板**用于为挑战生成 Kubernetes 部署文件。 **`kube-ctf` 部署模板**用于在使用 [kube-ctf](https://github.com/ctfpilot/kube-ctf) 平台时,为 instanced 挑战生成部署文件。在此模板中,嵌入了挑战部署模板。 ### 页面结构 每个页面都存储在仓库根目录下 `pages/` 目录中的独立文件夹中。 页面用于在 CTFd 中创建自定义页面,例如规则、关于页面或其他信息内容。 页面的子目录结构如下: ``` . ├── k8s/ ├── page.html (or page.md, page.txt) ├── page.yml (or page.json) └── version ``` - `k8s/` 包含页面的 Kubernetes ConfigMap 文件。这是由 `page` 命令自动生成的,不应手动修改。 - `page.html`(或 `page.md`、`page.txt`)包含页面的实际内容。文件名通过 `content` 字段在 `page.yml` 中指定。内容可以是 HTML 或 Markdown 格式。 - `page.yml` 包含页面的元数据。这必须由页面创建者填写。遵循由 [CTF Pilot 的页面 Schema](https://github.com/ctfpilot/page-schema) 定义的严格结构。 该文件可以替换为 JSON 文件,即 `page.json`。 - `version` 包含页面的版本。它由 `page` 命令自动更新,并包含代表该版本的单个数字。 ## 贡献 我们欢迎各种形式的贡献,从**代码**和**文档**到**错误报告**和**反馈**! 请查看[贡献指南 (`CONTRIBUTING.md`)](/CONTRIBUTING.md) 了解有关如何贡献的详细指南。 ### 运行测试 要运行测试套件,请确保你已安装所有开发依赖项。然后你可以使用 `pytest` 执行测试: ``` uv run pytest ``` ### 贡献者许可协议 (CLA) 为了保持在我们所有许可模型中分发贡献的能力,**所有代码贡献都需要签署贡献者许可协议 (CLA)**。 你可以在此处查看 **[CLA](https://github.com/ctfpilot/cla)**。当你创建第一个 pull request 时,系统会自动进行 CLA 签署。 为了管理 CLA 签署过程,我们使用了 **[CLA assistant lite](https://github.com/marketplace/actions/cla-assistant-lite)**。 *本仓库中也包含了 CLA 文档的副本,即 [`CLA.md`](CLA.md)。* *签名存储在 [`cla` 仓库](https://github.com/ctfpilot/cla) 中。* ## 许可证 本工具和仓库基于 **EUPL-1.2 许可证**授权。 你可以在 **[LICENSE](LICENSE)** 文件中找到完整的许可证。 我们鼓励将所有修改和贡献回馈给社区,例如通过向本仓库提交 pull request。 我们也鼓励所有衍生作品在 **EUPL-1.2 许可证**下公开提供。 在任何时候都必须遵守许可条款。 有关如何贡献的信息,请参阅上面的[贡献](#contributing)章节。 CTF Pilot 由 **[The0Mikkel](https://github.com/The0mikkel)** 拥有和维护。 所需声明:版权所有 Mikkel Albrechtsen () ## 行为准则 我们期望所有贡献者都能遵守我们的[行为准则](/CODE_OF_CONDUCT.md),以确保为所有人营造一个受欢迎和包容的环境。
标签:Docker, Python, 子域名突变, 安全防御评估, 文档结构分析, 无后门, 特权提升, 自动化部署, 请求拦截, 逆向工具