kaushalmeena/pocket-ctf
GitHub: kaushalmeena/pocket-ctf
一个纯浏览器端运行的开源 CTF 演练平台,内置十二道安全谜题,通过密码学手段确保 flag 不泄露在源码中。
Stars: 1 | Forks: 0

# Pocket CTF
[](LICENSE) [](https://www.solidjs.com) [](https://tanstack.com/start) [](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, 前端全栈, 安全靶场, 密码学, 手动系统调用, 自动化攻击, 隐写术