preeeetham/solana-ctf
GitHub: preeeetham/solana-ctf
一套包含 14 个 Solana 智能合约 CTF 挑战及完整题解的安全学习仓库,帮助开发者通过实战掌握 Solana 链上常见漏洞模式。
Stars: 0 | Forks: 0
# 🏴 Solana CTF — 夺旗赛挑战与题解
一份精心整理的 **14 个基于 Solana 的夺旗赛 (CTF)** 挑战合集,包含完整题解、漏洞利用脚本以及一份详尽的智能体指南。这些挑战涵盖了真实的 Solana 漏洞模式——从缺少 signer 检查到 DeFi预言机操纵——所有挑战均部署在 Solana **Devnet** 上。
## 📂 仓库结构
```
solana-ctf/
├── challenge-1/ # Ghost Admin (100 pts) — Missing signer check
├── challenge-2/ # Good First Impression (100 pts) — Vanity address grind
├── challenge-3/ # Logs of Truth (100 pts) — Input-dependent log emission
├── challenge-4/ # The Birthday Seed (100 pts) — Genesis timestamp PDA
├── challenge-5/ # Named by Numbers (200 pts) — Account data parsing
├── challenge-6/ # Signature Safari (200 pts) — Ed25519 verification
├── challenge-7/ # The Lamport Clock (200 pts) — Lamport value as timestamp
├── challenge-8/ # Where is the Needle? (250 pts) — Mass PDA account scanning
├── challenge-9/ # (Integer Overflow) (300 pts) — Arithmetic overflow
├── challenge-10/ # Monkeys and Bananas (250 pts) — NFT metadata tracing
├── challenge-11/ # Do Not Claim Thyself (300 pts) — Creator vs caller exploit
├── challenge-12/ # Voucher Roulette (300 pts) — MD5 hash brute-force
├── challenge-13/ # (Steganography) (400 pts) — RGB steganography
├── challenge-14/ # Sus Protocol (400 pts) — DeFi take_while vs filter bug
│
├── SOLUTIONS.md # Detailed write-ups for all solved challenges
├── CTF_V2_SPEC.md # Predicted v2 challenges (harder successors)
├── SOLANA_CTF_AGENT_GUIDE.md # Full agent knowledge base for solving CTFs
├── solutions_dashboard.html # Visual dashboard of all challenge scores
├── package.json # Node.js dependencies
├── tsconfig.json # TypeScript configuration
└── ctf_wallet.json # CTF solver wallet (DO NOT COMMIT REAL FUNDS)
```
每个 `challenge-N/` 目录通常包含:
- `question.txt` — 挑战题目与提示
- `public_source.rs` — 已部署的 Solana program 的 Rust 源码(如果提供)
- `solve.ts` — 漏洞利用/解题 TypeScript 脚本
- `explore*.ts` — 用于链上侦察的探测脚本
## 🏆 积分榜
| # | 标题 | 积分 | 漏洞 | Flag |
|---|-------|--------|---------------|------|
| 1 | Ghost Admin | 100 | 缺少 signer 检查 (`UncheckedAccount`) | `ST_FLAG{trust_n0_pubk3y}` |
| 2 | Good First Impression | 100 | 个性化地址前缀检查 | `ST_FLAG{k3yp41r_gr1nd1ng_ch4mp}` |
| 3 | Logs of Truth | 100 | 通过 `msg!()` 依赖输入输出 flag | `ST_FLAG{1sol_2sol_3sol_truth}` |
| 4 | The Birthday Seed | 100 | 使用 Solana 创世时间戳作为种子的 PDA | `ST_FLAG{ep0ch_0}` |
| 6 | Signature Safari | 200 | 跨越 500 个配对的 Ed25519 验证 | `ST_FLAG{s1g_ch4mp_d1d_th3_d1ff}` |
| 7 | The Lamport Clock | 200 | Lamport 转账金额作为 Unix 时间戳 | `ST_FLAG{a_ba1ance_b0rn_in_2006}` |
| 8 | Where is the Needle? | 250 | 使用 `getProgramAccounts` 扫描 500 个 PDA | `ST_FLAG{pda_hunt1ng_m4st3r}` |
| 10 | Monkeys and Bananas | 250 | NFT 交易链追踪 | `ST_FLAG{ca11ern0tc4eat0r}` |
| 11 | Do Not Claim Thyself | 300 | 调用者 ≠ 创建者 program 区分 | `ST_FLAG{ca11er_n0t_creat0r}` |
| 12 | Voucher Roulette | 300 | 逐位置 MD5 哈希暴力破解 | `ST_FLAG{g00d_on3}` |
| 14 | Sus Protocol | 400 | `take_while` 与 `filter` 引发的 DeFi 借贷漏洞 | `ST_FLAG{t00k_y0u_a_wh1le}` |
**累计得分:2300+ 积分**
## ⚡ 快速开始
### 前置条件
- **Node.js** ≥ 18
- **Solana CLI**(用于生成钱包及进行 CLI 交互)
- **TypeScript** / `ts-node`
### 1. 安装依赖
```
npm install
```
### 2. 设置解题钱包
```
# 生成新的 keypair
solana-keygen new --outfile ctf_wallet.json --no-bip39-passphrase
# 将 Solana CLI 指向 Devnet
solana config set --url devnet --keypair ctf_wallet.json
# Airdrop 测试 SOL
solana airdrop 5
# 或访问 https://faucet.solana.com
```
### 3. 运行题解脚本
每个挑战都有各自的 `solve.ts` 脚本。例如:
```
# 解决 Challenge 1 (Ghost Admin)
npx ts-node challenge-1/solve.ts
# 解决 Challenge 14 (Sus Protocol)
npx ts-node challenge-14/solve.ts
```
## 🔍 挑战概述
### 挑战 1 — Ghost Admin ★☆☆☆☆
**漏洞:** `admin` 账户使用了 `UncheckedAccount<'info>` 而非 `Signer<'info>`。该 program 检查了 `admin.key() == vault.admin`,但从未验证该账户是否实际签署了交易。
**利用方式:** 将 admin 的公钥作为 `admin` 账户传入,但无需使用它进行签名。
### 挑战 2 — Good First Impression ★☆☆☆☆
**漏洞:** 该 program 要求交易 signer 的公钥必须以 `bo1t` 开头。
**利用方式:** 运行 `solana-keygen grind --starts-with bo1t:1`,为生成的钱包充值,并使用它进行签名。
### 挑战 3 — Logs of Truth ★☆☆☆☆
**漏洞:** `verifyNumber` 指令会在特定的 lamport 输入值(1 SOL、2 SOL、3 SOL)下,通过 `msg!()` 输出部分 flag 字符串。
**利用方式:** 分别传入 `1_000_000_000`、`2_000_000_000` 和 `3_000_000_000` 来调用该指令,并拼接日志片段。
### 挑战 4 — The Birthday Seed ★☆☆☆☆
**漏洞:** Vault PDA 是使用 Solana 的创世区块时间戳(`1584368940`)作为种子派生出来的。
**利用方式:** 使用 `seeds = [b"vault", 1584368940_u64.to_le_bytes()]` 派生该 PDA,并读取其中存储的 flag。
### 挑战 6 — Signature Safari ★★☆☆☆
**漏洞:** 提供了 500 个 `signature::message` 配对;其中只有一个是给定公钥的有效 Ed25519 签名。
**利用方式:** 使用 `tweetnacl` 的 `sign.detached.verify()` 遍历所有配对以找到有效的那一个,然后在 Devnet 上查找该交易。
### 挑战 7 — The Lamport Clock ★★☆☆☆
**漏洞:** 一个特定的钱包转账了 `1152684000` lamports,这是一个表示 2006 年 7 月 12 日日期的 Unix 时间戳。
**利用方式:** 将 lamport 金额解码为 Unix 时间戳,推导出具体日期,并将其 POST 到 CTF 的提交端点。
### 挑战 8 — Where is the Needle? ★★☆☆☆
**漏洞:** 该 program 拥有的 500 个 PDA 账户中,其中一个包含了 flag。
**利用方式:** 使用 `getProgramAccounts` 获取所有账户,对每个账户进行反序列化(8 字节 discriminator + 32 字节 flag + 4 字节索引),并搜索 `ST_FLAG`。
### 挑战 10 — Monkeys and Bananas ★★★☆☆
**漏洞:** flag 被隐藏在 NFT 创建链的交易历史记录中,而不是在 NFT 本身。
**利用方式:** 追踪 NFT → 更新权限 → 资金钱包 → program 交互 → `ClaimFlag` 指令日志的路径。
### 挑战 11 — Do Not Claim Thyself ★★★☆☆
**漏洞:** 该 program 会奖励不是 token 创建者的调用者。flag 在第一笔 `GetEntry` 交易的日志中被输出。
**利用方式:** 从该 program 有史以来的第一笔交易签名中读取交易日志。
### 挑战 12 — Voucher Roulette ★★★☆☆
**漏洞:** 该 program 通过独立计算每个位置的 `MD5(char + position_index)` 来验证一个 17 字符的代码。每个位置都可以单独进行暴力破解。
**利用方式:** 对于每个位置(0–16),尝试所有 256 个字节值,计算 MD5,并与 `EXPECTED_HASHES[i]` 进行比较。
### 挑战 14 — Sus Protocol ★★★★☆
**漏洞:** 一个 DeFi 借贷协议使用 `.take_while()` 计算可见头寸(在遇到 `Pubkey::default()` 间隔时停止),但使用 `.filter()` 验证漏洞利用条件(检查所有头寸)。从中间位置提取头寸会制造一个视觉盲区。
**利用方式:** 存款 → 关闭中间头寸 → 越过间隔再次存款 → 针对“不可见的”真实抵押品进行多次借款,直到 `real_loans > max_allowed * 2`。
## 📚 核心文档
| 文件 | 描述 |
|------|-------------|
| [`SOLUTIONS.md`](./SOLUTIONS.md) | 所有 11 个已解挑战的详细步骤记录,包含终端输出及获得的 flag |
| [`CTF_V2_SPEC.md`](./CTF_V2_SPEC.md) | 预测的 v2 挑战(每个 v1 挑战的难度进阶版,总计 5,500 积分) |
| [`SOLANA_CTF_AGENT_GUIDE.md`](./SOLANA_CTF_AGENT_GUIDE.md) | 完整知识库:Solana 基础知识、探测手册、漏洞目录、漏洞利用模板 |
| [`solutions_dashboard.html`](./solutions_dashboard.html) | 可视化所有挑战分数和解题方案的交互式 HTML 仪表盘 |
## 🛠️ 技术栈
| 工具 | 用途 |
|------|---------|
| **TypeScript** + `ts-node` | 漏洞利用脚本编写和链上交互 |
| **`@coral-xyz/anchor`** | 用于 program 交互的 Anchor 框架客户端 |
| **`@solana/web3.js`** | 底层 Solana RPC 调用 |
| **`tweetnacl`** | Ed25519 签名验证 |
| **`pngjs`** | PNG 图像解析(隐写术挑战) |
| **Solana CLI** | 钱包管理、空领、交易检查 |
## 🔑 漏洞模式参考
| 模式 | 对应挑战 | 核心概念 |
|---------|-------------|-------------|
| 缺少 signer 检查 | 挑战 1 | `UncheckedAccount` 与 `Signer` 对比 |
| 个性化地址暴力生成 | 挑战 2 | `solana-keygen grind` |
| 依赖输入的日志输出 | 挑战 3 | 带有特定输入的 `msg!()` |
| 作为 PDA 种子的历史链上数据 | 挑战 4 | Solana 创世时间戳 `1584368940` |
| Ed25519 签名验证 | 挑战 6 | `tweetnacl.sign.detached.verify()` |
| Lamport 值编码 | 挑战 7 | Lamports 作为 Unix 时间戳 |
| 海量 PDA 账户扫描 | 挑战 8 | `getProgramAccounts` + `memcmp` 过滤 |
| NFT/交易链追踪 | 挑战 10 | 多跳账户关系图谱 |
| 创建者与调用者区分 | 挑战 11 | Program CPI 身份校验 |
| 哈希暴力破解(按位置) | 挑战 12 | `(char + index)` 的 MD5 |
| DeFi 迭代器不匹配 | 挑战 14 | `take_while` 相比 `filter` 制造了盲区 |
## 🔮 v2 挑战(预测版)
[`CTF_V2_SPEC.md`](./CTF_V2_SPEC.md) 记录了每个挑战的预测 **v2** 版本——旨在修复 v1 中被利用的漏洞。总计:涵盖 14 个难度更高挑战的 **5,500 积分**(对于经验丰富的 Solana 开发者约需 30–50 小时)。
| v1 漏洞利用 | v2 漏洞利用 |
|-----------|-----------|
| 缺少 signer 检查 | 重新初始化覆盖 |
| 简单前缀暴力生成 | PDA 派生前缀暴力生成 |
| 读取 `msg!()` 日志 | 解码 `set_return_data()` + XOR 解密 |
| 单一创世时间戳 | 多源链上考古 |
| 扁平化 Borsh 结构 | 版本化 schema + 加密 |
| 简单 Ed25519 验证 | 部分签名重构 |
| 500 个账户,1 个 flag | 50,000 个账户,100 个诱饵 + bump 验证 |
| `take_while` 与 `filter` | 通过未验证账户进行预言机价格操纵 |
## ⚠️ 免责声明
本仓库仅供**教育目的**使用。所有挑战均在 Solana **Devnet**(测试网)上运行,不包含任何实际价值。此处展示的漏洞利用技术是 CTF 比赛和安全研究中常见的漏洞模式。请勿将这些技术应用于主网 program 或真实资产。
## 📄 许可证
MIT
标签:Anchor, MITM代理, Solana, TypeScript, 区块链安全, 可视化界面, 后端开发, 安全插件, 智能合约审计