kaushalmeena/pocket-ctf

GitHub: kaushalmeena/pocket-ctf

一个纯浏览器端运行的开源 CTF 演练平台,内置十二道安全谜题,通过密码学手段确保 flag 不泄露在源码中。

Stars: 1 | Forks: 0

Pocket CTF logo # Pocket CTF [![License: MIT](https://img.shields.io/badge/License-MIT-3DA639?logo=opensourceinitiative&logoColor=white)](LICENSE) [![Solid](https://img.shields.io/badge/Solid-1.9-2C4F7C?logo=solid&logoColor=white)](https://www.solidjs.com) [![TanStack Start](https://img.shields.io/badge/TanStack_Start-1.x-C2255C?logo=react&logoColor=white)](https://tanstack.com/start) [![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-4-0B7285?logo=tailwindcss&logoColor=white)](https://tailwindcss.com) **十二个安全谜题,一个浏览器,源码中找不到半个 flag。** 一个免费、开源的夺旗赛(capture-the-flag)演练场,完全在你的 机器上运行——无需服务器、无需账号、无需联网。每个 flag 都以 **加盐 PBKDF2 哈希** 的形式存储,并且每个谜题的内容都是在构建时根据其 flag 推导出来的, 因此答案确实不在你下载的文件包中。 [**在线体验**](https://pocket-ctf.vercel.app)
## 功能 - **十二个精心打造的挑战** — 隐写术、多层编码、摩斯 电码音频、质因数分解、重复密钥 XOR、LFSR 流密码、 字节码 VM、CSS、SVG、IndexedDB 取证以及浏览器 devtools。 - **没有明文 flag** — 每个 flag 仅以加盐哈希的形式存在;零宽 度诗歌、隐写 PNG、质数乘积和 XOR 密文都是从 flag 计算*出来的*,因此答案无处藏身。 - **防篡改进度** — 解答过程会存储一个证明解题的 token,而不是一个 布尔值。修改 localStorage 毫无用处,并且站点会告诉你它 丢弃了什么。 - **加密的解析** — 解答后会解密出相关技术的解释及其原理。 在此之前,文件包中根本不存在该明文。 - **不可猜测、每个部署唯一的 flag** — 每个挑战的随机后缀意味着没有 两个安装实例共享同一个答案,因此从其他地方复制来的答案会失效。 - **完全离线运行** — 一堆静态 HTML 文件,你可以托管在任何地方或 从 USB 闪存盘打开。你输入的任何内容都不会离开本机。 - **即插即用的挑战** — 每个谜题一个目录,其中包含其元数据、 构建器、参考求解器和页面。添加一个挑战只需添加一个文件夹;删除 一个挑战只需删除其文件夹。没有其他需要保持同步的列表。 - **提示、积分、计时器和徽章** — 每个挑战提供三个消耗积分的递进式提示,十枚 徽章,以及单挑战计时器。 - **浅色和深色主题** — 在你另行设定之前,它会遵循你的系统偏好, 并且在加载时不会出现错误主题的闪烁。 ## 工作原理 1. **编写** — 你将基础 flag 写入被 gitignore 的 `flags.secret.json` 中。 2. **生成** — `pnpm gen` 会为每个 flag 推导出一个加盐 PBKDF2 验证 哈希、一个证明解题的 token 摘要、一个用于加密解析的 AES-GCM 密钥、 一个每次部署随机的后缀,以及谜题 payload 本身。 3. **解答** — 玩家使用 devtools、控制台单行命令、笔 和纸或耳朵来解开谜题。这里没有指定的工具,只有指定的洞察力。 4. **验证** — 对提交的内容进行哈希处理并进行比较。正确的答案会存储一个 证明已解答的 token,而无需记录 flag 本身。 ## 技术栈 | 领域 | 工具 | | ------------- | ----------------------------------------------------------------------------------------- | | **Framework** | [Solid](https://www.solidjs.com) · [TanStack Start](https://tanstack.com/start) | | **样式** | [Tailwind CSS v4](https://tailwindcss.com) · [Lucide](https://lucide.dev) | | **密码学** | [Web Crypto](https://developer.mozilla.org/docs/Web/API/Web_Crypto_API) (PBKDF2, AES-GCM) | | **构建** | [Vite](https://vite.dev) · [TypeScript](https://www.typescriptlang.org) | | **测试** | [Vitest](https://vitest.dev) | | **工具** | [Biome](https://biomejs.dev) · [pnpm](https://pnpm.io) | | **托管** | 任何静态主机 — 构建产物就是一个包含许多文件的文件夹 | ## 入门指南 这些说明将帮助你在本地计算机上复制并运行该项目,以进行 开发。 ### 前置条件 要安装和运行此项目,你需要: - [Node.js](https://nodejs.org/) 20 或更高版本 - [pnpm](https://pnpm.io/installation) - [git](https://git-scm.com/downloads)(仅用于克隆此代码库) ### 安装说明 要在本地计算机上完成所有设置,请按照以下步骤操作: 1. 克隆此代码库,然后将目录更改为 `pocket-ctf` 文件夹: ``` git clone https://github.com/kaushalmeena/pocket-ctf.git cd pocket-ctf ``` 2. 使用 pnpm 安装项目依赖: ``` pnpm install ``` 安装过程还会初始化挑战数据。全新克隆的版本会运行 **演示** 集合, 其中的 flag 在 `flags.sample.json` 中是公开的,并且应用会通过 横幅标明它们。要创建具有不可猜测 flag 的真实实例,请编写你自己的答案 密钥并据此生成: ``` cp flags.sample.json flags.secret.json # 然后替换每一个短语 -- 已发布 demo 版本 pnpm gen ``` 你解析出的 flag 会被写入 `ANSWERS.secret.md`,该文件已被 gitignore —— 这是 唯一可读的副本,因此请在代码库之外保留备份。 ### 运行 要运行项目,只需执行: ``` pnpm dev ``` 你的应用现在应该正在 [localhost:3000](http://localhost:3000/) 上运行。 ### 测试 测试套件在 [Vitest](https://vitest.dev) 下运行。关键测试会驱动 每个挑战自己的参考求解器去处理实际发布的 payload, 因此无法解开的谜题会导致构建失败: ``` pnpm test ``` 要同时执行 lint、类型检查、扫描泄露的 flag 并运行测试套件: ``` pnpm check ``` `pnpm check` 会针对当前生效的任何挑战数据运行,在全新克隆的 版本中,这即是已提交的演示集合。一旦你生成了自己的 flag,请验证*那个* 部署——每个挑战都通过其发布的 payload 解开,并且每个答案 都经过与浏览器执行的相同检查: ``` pnpm self-test ``` 它拒绝在演示集合上运行,因此绿色的结果始终是针对你自己的 flag 的。 ### 构建 要创建生产构建: ``` pnpm build ``` 构建输出会被写入 `dist/client` 文件夹 —— 这是任何 文件服务器都可以托管的静态 HTML。使用 `pnpm preview` 在本地预览,并使用 `pnpm build && pnpm verify:flags` 针对真实的构建产物重新运行泄露 扫描。 ## 部署 每个路由都是预渲染的,因此没有需要部署的服务器 runtime。`pnpm build` 会写入 `dist/client`,这是一个纯静态文件文件夹,任何主机都能提供服务—— 将其指向该目录,启用 clean URLs,就大功告成了。`vercel.json` 是 针对某个主机的这些设置的现成示例;如果你部署在 其他地方,请将其删除。 托管构建会克隆代码库,因此被 gitignore 的答案密钥将不存在, 部署时会发布演示集合。运行 `pnpm gen:env --url=https://your-origin` 将 这些密钥写入被 gitignore 的 `.env` 中,将它们交给你的主机, `scripts/bootstrap.mjs` 将在 `prebuild` 期间生成此部署自己的数据。完整细节,包括为什么这两个密钥变量都是 必需的,以及主机需要哪些设置,都在 [docs/architecture/deployment.md](docs/architecture/deployment.md) 中。 ## 配置 | 变量 | 默认值 | 备注 | | ------------------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `POCKET_CTF_FLAGS` | `—` | `flags.secret.json` 的全部内容,用于托管构建。没有它,部署时将发布公开的演示集合。 | | `POCKET_CTF_SEEDS` | `—` | `seeds.secret.json` 的全部内容。与上面的变量**必须同时配置**,否则每次部署都会更改你的 flag。 | | `VITE_SITE_URL` | `http://localhost:3000` | 你的公开 origin,用于规范 URL、站点地图和社交分享卡片。**任何公开部署都请务必设置此项** — `pnpm gen` 会在缺失时发出警告。 | ## 文档 - **[架构](docs/architecture/README.md)** — 一个答案密钥如何变成哈希、密文和谜题数据,如何添加和删除挑战,每一项反作弊措施及其面临的限制,以及如何在不提交答案的情况下部署真实实例。 - **[设计](docs/design/README.md)** — 界面决策及其背后的原因,涵盖颜色与主题、动画、图标、组件模式、可访问性和语气。 ## 贡献 欢迎任何贡献!如果你发现 bug 或有功能建议,请 先[创建一个 issue](https://github.com/kaushalmeena/pocket-ctf/issues/new/choose) 进行讨论。对于代码更改,请 fork 该代码库,创建一个分支,并 提交 pull request。请不要将 flag 或完整的解决方案粘贴到 issue 或 pull request 中。 ## 许可证 该项目基于 MIT 许可证授权 — 有关详细信息,请参阅 [LICENSE](LICENSE) 文件。
标签:Solid, 前端全栈, 安全靶场, 密码学, 手动系统调用, 自动化攻击, 隐写术