at0m-b0mb/Gauntlet-FlipperZero
GitHub: at0m-b0mb/Gauntlet-FlipperZero
Flipper Zero 上的设备端 CTF 挑战平台,支持加载自定义题包并在设备上直接解谜与提交 Flag。
Stars: 1 | Forks: 0

# Gauntlet
**一个运行在 Flipper Zero 设备上的夺旗赛(Capture-The-Flag)盒子。**
从 SD 卡加载挑战包,直接在设备上解决 **RFID**、**红外线** 和 **密码** 谜题 —— 房间里不需要笔记本电脑。内置的解码器 **Toolkit** 可以在屏幕上破解密码,Flag 会通过哈希值进行校验以保密答案,同时你的分数、排名和已解决数量会跨会话保存。
[](https://github.com/at0m-b0mb/Gauntlet-FlipperZero/actions/workflows/build.yml)


-8a2be2)


## 这是什么
**Gauntlet 能把你的 Flipper 变成一个自成一体的 CTF 街机。** 将一个挑战包——一个纯文本的 `.pack` 文件——放入 SD 卡,应用就会变成一个可在设备上解决的谜题菜单:解码捕获的红外帧、从 Wiegand 徽章转储中提取设施代码、破解凯撒密码。输入 Flag,看着它被捕获,提升你的排名。
它是为 **研讨会、教室和 CTF 练习** 设计的,在这些场景下给每个人发一台笔记本电脑是不切实际的 —— 挑战 *和* 解决挑战的工具都在 Flipper 上。

*主菜单 · 一项挑战 · 正在破解密码的 Toolkit · Flag 已捕获*
## 核心亮点
- 🚩 **加载挑战包** —— 可读性强的 `.pack` 文件,你可以使用任何文本编辑器编写并分享。
- 🧰 **内置解码器 Toolkit** —— ROT/凯撒(所有 25 种偏移)、Base64、Hex、Binary、Morse、Atbash 和倒序。无需任何外部工具,直接在屏幕上破解密码任务。
- 🔐 **哈希化 Flag** —— 答案以哈希值存储,因此读取 `.pack` 文件的解题者也无法看到解决方案。
- 🏆 **分数、排名与持久化** —— 每次解决都会保存到 SD 卡。赚取积分,从 **新手** 晋升为 **传奇**。
- 💡 **提示** —— 每个挑战可选的提示功能,你可自控是否扣除积分惩罚。
- 🎨 **量身定制的 UI** —— 带分类标签的标题栏、难度标识、可滚动的挑战阅读器、解码器轮播以及生动的 *Flag 已捕获* 庆祝动画。
- 📦 **开箱即玩** —— 首次运行时会在你的 SD 卡上写入一个入门挑战包。
- 🛡️ **无需额外硬件** —— 纯软件运行;无需接线。
## 挑战分类
| | 类别 | 挑战示例 |
|:---:|---|---|
| 🔡 | **密码 (Cipher)** | 凯撒 / ROT、Base64、Hex、Binary、Morse、Atbash —— 可用 Toolkit 解决 |
| 💳 | **RFID** | 解码卡片数据块,从 26 位 Wiegand 读取中提取设施代码 |
| 📡 | **红外线 (Infrared)** | 从捕获的 NEC 帧中读取数值,解码隐藏的遥控器标签 |
| 📻 | **Sub-GHz** | 无线电风味的解码谜题 |
| 🧩 | **杂项 (Misc)** | 热身题和逻辑谜题 |
入门挑战包包含了以上所有类别的探索之旅。
## 工作原理
```
flowchart LR
A[.pack files on SD] --> B[Pack loader]
B --> C[Challenge browser]
C --> D[Challenge readerprompt · payload · hint] D -->|Right| E[Decoder Toolkit
ROT/B64/Hex/Bin/Morse] D -->|OK| F[Enter flag] E -->|OK: use output| F F --> G{hash matches?} G -->|yes| H[[FLAG CAPTURED
+points · save · rank up]] G -->|no| D ``` 每个挑战都包含一个 **类别**、**分数**、**难度**、**提示语**、可选的 **有效载荷** (`DATA`) 以及 **线索**。你阅读它,解决它(如果是密码,可以使用 Toolkit),然后提交 Flag。Gauntlet 会对你的答案进行标准化处理(去除空格,除非挑战标记为 `EXACT` 否则不区分大小写),计算其哈希值,并与存储的哈希值进行比较。匹配 → Flag 被捕获,积分被存入并保存。 ## Toolkit 从任意挑战(按 **右键**)打开它时会预加载该挑战的有效载荷,或者从主菜单输入自由文本打开。使用 **← →** 切换解码器,对于 ROT,使用 **↑ ↓** 拨动偏移量并观察明文归位。按 **OK** 可将当前输出直接放入 Flag 输入框。 | 解码器 | 功能 | |---|---| | **ROT-N / 凯撒** | 移动字母表;遍历所有 25 种偏移 | | **Base64** | 解码 Base64 文本 | | **Hex → 文本** | 将十六进制字节对转换为 ASCII | | **Binary → 文本** | 将 8 位分组转换为 ASCII | | **Morse → 文本** | 将 `.` `-` 和 `/` 转换为字母 | | **Atbash** | 镜像字母表 (A↔Z) | | **倒序** | 反转字符串 | ## 安装 ### 选项 A —— qFlipper(最简单) 1. 从 [最新发布](https://github.com/at0m-b0mb/Gauntlet-FlipperZero/releases) 下载 `gauntlet.fap`(或按照下文自行构建)。 2. 打开 **qFlipper** 并连接你的 Flipper Zero。 3. 将 `gauntlet.fap` 拖入 **SD Card → `apps` → `Games`**。 4. 在 Flipper 上:**Apps → Games → Gauntlet**。 首次启动会将一个入门包写入 `apps_data/gauntlet/packs/`,因此可以立即开始游戏。 ### 选项 B —— 使用 ufbt 构建并启动 ``` python3 -m pip install --upgrade ufbt # one-time git clone https://github.com/at0m-b0mb/Gauntlet-FlipperZero.git cd Gauntlet-FlipperZero ufbt # builds dist/gauntlet.fap ufbt launch # installs + opens it on a connected Flipper ``` ### 选项 C —— 手动 SD 卡 将 `dist/gauntlet.fap` 复制到 Flipper microSD 卡的 `apps/Games/` 目录下,然后从 **Apps → Games → Gauntlet** 打开它。 ## 使用方法 1. **Play** → 选择一个 **挑战包** → 选择一项 **挑战**。 2. 阅读提示语和有效载荷。使用 **↑ ↓** 滚动。卡住了?按 **左键** 查看线索。 3. 如果是密码题,按 **右键** 在有效载荷上打开 **Toolkit**,进行解码,然后按 **OK** 将结果发送到 Flag 输入框。 4. 在挑战界面按 **OK** 输入 Flag。如果输入正确,Flag 就会被 **捕获** —— 积分存入,进度保存。 5. 从主菜单查看 **Progress** 了解你的分数、排名和已捕获的 Flag。 **挑战界面控制:** `OK` 输入 Flag · `Right` 打开 Toolkit · `Left` 查看线索 · `Up/Down` 滚动 · `Back` 返回列表。 **Toolkit 控制:** `Left/Right` 切换解码器 · `Up/Down` 滚动(或调整 ROT 偏移) · `OK` 使用当前输出作为 Flag。 ## 制作你自己的挑战包 挑战包是一个以 `.pack` 结尾的纯文本文件,放置在以下位置: ``` SD:/apps_data/gauntlet/packs/ ``` 它是基于行的格式 —— 每行一个 `KEY value`。一个 `CHALLENGE` 行开始一个新的挑战;其下方的键值对描述该挑战,直到遇到下一个 `CHALLENGE`。 ``` PACK My Workshop AUTHOR you DESC A short description of the pack CHALLENGE Caesar's Secret CATEGORY cipher # cipher | rfid | ir | radio | misc POINTS 100 DIFFICULTY easy # easy | medium | hard | insane PROMPT Julius left a note. Roll it back and submit the name. DATA WKH QDPH LV FDHVDU HINT Every letter moved by 3. Try the Toolkit's ROT. FLAG caesar ``` **可用键:** `PACK`, `AUTHOR`, `DESC`(挑战包级别) · `CHALLENGE`, `CATEGORY`, `POINTS`, `DIFFICULTY`, `PROMPT`, `DATA`, `HINT`, `FLAG`/`ANSWER`, `EXACT`(单个挑战级别)。`PROMPT` 和 `DATA` 可以重复使用以增加多行。以 `#` 开头的行是注释。 ### 对答案保密 `FLAG plaintext` 是最简单的方法 —— Gauntlet 会在加载时对其进行哈希处理。但好奇的解题者可能会打开文件直接读取答案。为了确保答案永远不会出现在挑战包中,你可以提供一个 **预计算的哈希值**: ``` ANSWER a1b2c3d4 # FNV-1a 32-bit of the normalised flag ``` 使用内置工具生成哈希值(以及一个可直接使用的入门挑战包): ``` python3 tools_gen_pack.py # prints each challenge's ANSWER hash ``` 在挑战中添加 `EXACT` 可使其 Flag 区分大小写。请参阅 [`packs/starter.pack`](packs/starter.pack) 获取完整且有效的示例。 ## 安全、道德与法律 ## 从源码构建 需要 [ufbt](https://pypi.org/project/ufbt/)。资源和入门包使用 Python + Pillow 重新生成。 ``` ufbt # build dist/gauntlet.fap python3 tools_gen_pack.py # starter.pack + embedded ctf_starter.h python3 tools_gen_icons.py # 10px app icon python3 tools_gen_banner.py # README banner + social preview python3 tools_gen_mockups.py # screen mockups ``` ``` Gauntlet-FlipperZero/ ├── application.fam # app manifest (Games) ├── gauntlet.c / gauntlet_i.h # app lifecycle + state ├── helpers/ │ ├── ctf_codec.c/.h # the decoder Toolkit (pure) │ ├── ctf_pack.c/.h # pack model + parser + flag hashing (pure) │ ├── ctf_store.c/.h # SD-card I/O, progress, first-run starter │ └── ctf_starter.h # generated: embedded starter pack ├── views/ │ ├── challenge_view.c/.h # the challenge reader │ ├── toolkit_view.c/.h # the decoder carousel │ └── result_view.c/.h # the "flag captured" celebration ├── scenes/ # start · packs · browse · challenge · solve · │ # result · toolkit · toolinput · progress · │ # settings · about └── packs/starter.pack # a playable tour of every category ```
**Gauntlet** · 作者 [at0m-b0mb](https://github.com/at0m-b0mb) · MIT License
*闯过难关。夺取 Flag。*
标签:Flipper Zero, RFID, 客户端加密, 密码学, 嵌入式, 手动系统调用, 游戏, 红外通信, 逆向工具