免责声明:本文档为CTF(Capture The Flag)网络安全竞赛技术学习资料,旨在帮助安全从业者系统提升各方向手工解题能力。文中涉及的所有技术方法、工具使用及攻击手法,仅适用于合法授权的CTF竞赛及安全测试场景。严禁用于任何未经授权的系统入侵、数据窃取或破坏行为。请严格遵守《中华人民共和国网络安全法》及相关法律法规,合理使用安全技术。
CTF 比赛环境搭建与手工解题实战指南
本文档覆盖 环境搭建 + 全方向手工解题流程,以 Ubuntu 22.04 为基准环境,附 macOS 适配说明。文中穿插三道实战题目的完整解题过程,确保"看完就能动手"。
目录
- 第一部分:环境搭建
- 第二部分:通用解题流程
- 第三部分:Crypto 方向手工解题
- 第四部分:Reverse 方向手工解题
- 第五部分:Misc 方向手工解题
- 第六部分:Web 方向手工解题
- 第七部分:Pwn 方向手工解题
- 第八部分:Auto / Loot 自动化
- 附录:速查表与命令合集
第一部分:环境搭建
1.1 系统准备
推荐系统:Ubuntu 22.04 LTS (x86_64)
# 更新系统
sudo apt-get update && sudo apt-get upgrade -y
# 安装基础工具
sudo apt-get install -y \
git curl wget vim nano tmux htop tree \
build-essential gcc g++ make cmake \
python3 python3-pip python3-venv \
nodejs npm \
docker.io docker-compose \
unzip p7zip-full \
libgmp-dev libmpfr-dev libmpc-dev \
openssl libssl-dev
# 验证
python3 --version # >= 3.10
node --version
docker --version
macOS 用户:使用 Homebrew + Colima 替代 Docker Desktop,参见文末 macOS 适配章节。
1.2 核心工具一键安装
# Python 密码学与数学库
pip3 install --user --break-system-packages \
sympy numpy pycryptodome cryptography gmpy2 \
requests beautifulsoup4 lxml pillow pytesseract \
pwntools ropgadget
# 验证
python3 -c "import sympy, Crypto, gmpy2, numpy, pwn; print('Core OK')"
1.3 各方向专用工具
| 方向 | 工具 | 安装命令 |
|---|---|---|
| Crypto | PARI/GP, GMP-ECM, YAFU (Docker) | sudo apt install pari-gp gmp-ecm |
| Misc | steghide, binwalk, foremost, exiftool | sudo apt install steghide binwalk foremost exiftool |
| Web | Burp Suite, sqlmap, dirsearch | sudo apt install sqlmap + 手动安装 Burp |
| Pwn | pwndbg, Ghidra, checksec | pip install pwntools checksec.py |
| Reverse | Radare2, Cutter, Ghidra | sudo apt install radare2 cutter |
| Auto | selenium, tesseract, hashcat | pip install selenium pytesseract |
1.4 环境验证
# 运行验证脚本
python3 << 'EOF'
checks = {
"sympy": "import sympy",
"pycryptodome": "import Crypto",
"gmpy2": "import gmpy2",
"pwntools": "from pwn import *",
"requests": "import requests",
"PIL": "from PIL import Image",
}
all_ok = True
for name, cmd in checks.items():
try:
exec(cmd)
print(f"[OK] {name}")
except Exception as e:
print(f"[FAIL] {name}: {e}")
all_ok = False
print("\n[+] 全部通过" if all_ok else "\n[!] 部分失败")
EOF
第二部分:通用解题流程
2.1 拿到题目后的第一件事
1. 解压题目,查看文件列表
2. file * 查看所有文件类型
3. strings -n 8 * 提取可打印字符串
4. 如果是压缩包,检查是否有密码提示
5. 如果有图片/音频,检查 EXIF 信息
2.2 信息收集清单
每道题目必查项:
2.3 常见文件类型速查
| 文件头 (Hex) | 类型 | 识别命令 |
|---|---|---|
50 4B 03 04 |
ZIP | file |
89 50 4E 47 |
PNG | file |
FF D8 FF |
JPEG | file |
47 49 46 38 |
GIF | file |
7F 45 4C 46 |
ELF | file |
4D 5A / 50 4B |
PE / EXE | file |
52 61 72 21 |
RAR | file |
第三部分:Crypto 方向手工解题
3.1 RSA 攻击方法速查表
拿到 RSA 题目后,按以下顺序排查:
| 检查项 | 方法 | 工具/脚本 |
|---|---|---|
| n 的位数 | < 512 bit → 直接分解 | YAFU, CADO-NFS |
| e 是否很小 | e = 3 → 低指数攻击 | sympy.nthroot_mod |
| e 是否很大 | e ≈ n → Wiener 攻击 | 连分数展开 |
| n 是否近邻素数 | p ≈ q → Fermat 分解 | Python gmpy2.isqrt |
| 是否有多个 n | GCD 找公因子 | math.gcd(n1, n2) |
| n 是否多素数 | 分解后有 >2 个因子 | sympy.factorint |
| e 和 phi(n) 是否互质 | gcd(e, phi) ≠ 1 | math.gcd(e, phi) |
| 密文是否很小 | m^e < n → 直接开方 | gmpy2.iroot(c, e) |
3.2 实战:多素数 RSA + 低指数攻击
题目:0CTF 2016 Quals rsa-2 (GFSJ0784)
拿到题目后:
$ ls
flag.enc public.pem
$ openssl rsa -pubin -in public.pem -text -noout
Public-Key: (314 bit)
Exponent: 3 (0x3)
排查过程:
- e = 3 是危险信号,n 只有 314 bit 也是
- FactorDB 分解 n,发现 三个素数
- 尝试标准 RSA 解密:
inverse(3, phi)报错,因为gcd(3, phi) = 3 - 改用 立方根 + CRT:
sympy.nthroot_mod(c, 3, p, all_roots=True)
关键代码:
from sympy import nthroot_mod
p1, p2, p3 = 26440615366395242196516853423447, \
27038194053540661979045656526063, \
32581479300404876772405716877547
# p2 域:唯一立方根(p2 % 3 == 2)
d2 = pow(3, -1, p2 - 1)
r2 = pow(c, d2, p2)
# p1, p3 域:各有 3 个立方根
r1_list = nthroot_mod(c % p1, 3, p1, all_roots=True)
r3_list = nthroot_mod(c % p3, 3, p3, all_roots=True)
# CRT 组合 9 种可能
def crt(m1, m2, m3):
M1, M2, M3 = p2*p3, p1*p3, p1*p2
inv = lambda a, m: pow(a, -1, m)
return (m1*M1*inv(M1,p1) + m2*M2*inv(M2,p2) + m3*M3*inv(M3,p3)) % n
Flag:0ctf{HahA!Thi5_1s_n0T_rSa~}
3.3 实战:Fermat 分解法秒破 4096-bit RSA
题目:某 CTF RSA 题 (4096-bit, e=65537)
排查过程:
- e = 65537,n = 4096 bit,看起来标准
- Wiener 攻击失败(d 不够小)
- 检查
sqrt(n)附近,发现(sqrt(n)+1)^2 - n很小 - Fermat 分解:从
x = sqrt(n) + 1开始,检查x^2 - n是否完全平方
import gmpy2
sqrt_n = int(gmpy2.isqrt(n))
for i in range(1000000):
x = sqrt_n + i
y_sq = x * x - n
y = int(gmpy2.isqrt(y_sq))
if y * y == y_sq:
p, q = x + y, x - y
print(f"p={p}, q={q}")
break
结果:仅尝试 1 次就成功,p - q = 200,426,748(仅 28 bit)
标准 RSA 解密:
from Crypto.Util.number import inverse, long_to_bytes
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
Flag:flag{RSA_is_Easy_for_me}
3.4 对称加密与哈希
| 场景 | 识别方法 | 解法 |
|---|---|---|
| 已知密文 + 密钥 | 题目直接给出 | 直接解密 |
| 已知密文 + 弱密钥 | 密钥是常见字符串 | 暴力尝试 |
| RC4 流密码 | 字节分布均匀 | 提取密钥后标准解密 |
| AES-ECB | 相同明文块 → 相同密文块 | 块重排攻击 |
| 哈希碰撞 | 长度扩展 / 生日攻击 | hashpump / 脚本 |
第四部分:Reverse 方向手工解题
4.1 静态分析流程
拿到二进制后的标准操作:
# 1. 识别文件类型
file challenge.exe
# 2. 提取字符串
strings -n 8 challenge.exe | grep -iE "flag|key|input|correct|wrong|password"
# 3. 查看段信息
objdump -h challenge.exe
# 4. 查看导入/导出函数
objdump -p challenge.exe
# 5. 反汇编入口点
objdump -d --start-address=0xXXXX challenge.exe | head -50
4.2 实战:逆向 RC4 加密程序
题目:crypt.exe (PE32+ x86-64)
分析过程:
strings发现 PDB 路径c++_rc4.pdb→ 确认是 RC4- 提取关键字符串:密钥
12345678abcdefghijklmnopqrspxyz、error、nice job - 用 Capstone 搜索引用这些字符串的
lea指令,定位 main 函数 - 反汇编发现加密流程:
RC4_KSA(key, 32)初始化 S-boxRC4_PRGA(plaintext, inputlen)加密- 每字节
XOR 0x22 - 与
.data段 22 字节密文比较
解密:
def rc4(key, data):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
i = j = 0
result = bytearray()
for byte in data:
i = (i + 1) % 256; j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
result.append(byte ^ S[(S[i] + S[j]) % 256])
return bytes(result)
key = b"12345678abcdefghijklmnopqrspxyz"
ct = bytes.fromhex("9ee7305fa701a653591b0a20f173d10eab09840e8d2b")
flag = rc4(key, bytes([b ^ 0x22 for b in ct]))
print(flag) # flag{nice_to_meet_you}
4.3 动态调试基础
Linux ELF:
# 检查保护机制
checksec ./challenge
# GDB + pwndbg
gdb ./challenge
pwndbg> start
pwndbg> disas main
pwndbg> b *0xaddress
pwndbg> run
pwndbg> x/10gx $rsp
pwndbg> ni # 单步执行
Windows PE:
- x64dbg (Windows)
- IDA Pro + debugger
- 或者用 wine + gdb(Linux 上)
第五部分:Misc 方向手工解题
5.1 文件分析与数据恢复
# 检查文件真实类型
file unknown.bin
# 提取嵌入文件
binwalk -e unknown.bin
foremost -i unknown.bin -o output/
# 十六进制查看
xxd unknown.bin | head -20
# 修复文件头(如果头被破坏)
# 例如 PNG 头: 89 50 4E 47 0D 0A 1A 0A
python3 -c "
with open('broken.png', 'rb') as f:
data = f.read()
with open('fixed.png', 'wb') as f:
f.write(bytes([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]) + data[8:])
"
5.2 隐写分析
# LSB 隐写(PNG)
stegsolve (Java 工具,GUI)
# 检查不同颜色通道
python3 -c "
from PIL import Image
img = Image.open('stego.png')
# 提取 LSB
"
# steghide(需要密码)
steghide extract -sf image.jpg -p "password"
# zsteg(PNG/BMP)
zsteg image.png
# exiftool 查看元数据
exiftool image.jpg
5.3 流量分析
# 用 Wireshark 打开 pcap
# 过滤 HTTP 流量: http
# 提取文件: File -> Export Objects -> HTTP
# tshark 命令行
tshark -r capture.pcap -Y "http" -T fields -e http.host -e http.request.uri
# 提取特定流
tshark -r capture.pcap -Y "tcp.stream==0" -T fields -e data
第六部分:Web 方向手工解题
6.1 信息收集与目录扫描
# 目录扫描
python3 dirsearch.py -u http://target.com -e php,txt,zip,bak
# 检查常见文件
for f in robots.txt .git/ .svn/ .DS_Store backup.zip; do
curl -s -o /dev/null -w "%{http_code}" http://target.com/$f
done
# 查看响应头
curl -I http://target.com
6.2 SQL 注入
手动测试:
' OR '1'='1
' UNION SELECT 1,2,3-- -
' UNION SELECT null,table_name,null FROM information_schema.tables-- -
sqlmap 自动扫描:
sqlmap -u "http://target.com/page.php?id=1" --dbs
sqlmap -u "http://target.com/page.php?id=1" -D database --tables
sqlmap -u "http://target.com/page.php?id=1" -D database -T users --dump
6.3 XSS 与前端安全
手动测试:
<script>alert(1)</script>
<img src=x onerror=alert(1)>
javascript:alert(1)
检查 CSP 头:
curl -I http://target.com | grep -i content-security
第七部分:Pwn 方向手工解题
7.1 二进制安全检查
checksec ./challenge
常见输出解读:
| 保护 | 含义 | 绕过思路 |
|---|---|---|
| NX enabled | 栈不可执行 | ROP / ret2libc |
| Canary found | 栈 cookie | leak canary / 覆盖指针 |
| PIE enabled | 地址随机化 | leak 地址 / partial overwrite |
| RELRO Full | GOT 只读 | 不能改 GOT,用 ROP |
| FORTIFY | 强化检查 | 找未保护的函数 |
7.2 栈溢出基础
手动找偏移:
from pwn import *
# 生成 pattern
pattern = cyclic(200)
# 发送 pattern,观察崩溃时的寄存器值
# 例如 EIP = 0x61616161 → 找偏移
cyclic_find(0x61616161)
简单栈溢出(无保护):
from pwn import *
p = process('./challenge')
payload = b'A' * offset + p32(shellcode_address)
p.sendline(payload)
p.interactive()
7.3 利用脚本编写
模板:
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
# p = process('./challenge')
p = remote('target.com', 1337)
# 接收菜单
p.recvuntil(b'choice:')
p.sendline(b'1')
# 构造 payload
payload = b'A' * 0x40 + p64(0xdeadbeef)
p.sendline(payload)
p.interactive()
第八部分:Auto / Loot 自动化
8.1 自动化脚本框架
import requests
import re
import time
BASE = "http://target.com"
s = requests.Session()
# 登录
r = s.post(f"{BASE}/login", data={"user": "admin", "pass": "admin"})
print(r.text)
# 抓取页面
r = s.get(f"{BASE}/page")
flag = re.search(r'flag\{[^}]+\}', r.text)
if flag:
print(f"Found: {flag.group()}")
8.2 常见自动化场景
| 场景 | 方法 |
|---|---|
| 批量爆破密码 | requests + 字典 |
| 爬取所有页面 | BeautifulSoup + BFS |
| 验证码识别 | pytesseract (OCR) |
| 自动化浏览器 | selenium |
| 批量哈希计算 | hashlib |
附录:速查表与命令合集
A. 常用 Python 密码学代码片段
# 快速分解小 n
from sympy import factorint
factorint(n)
# 计算模逆元
pow(e, -1, phi)
# 大整数开方
import gmpy2
gmpy2.iroot(c, 3) # 立方根
# 字节 ↔ 整数
int.from_bytes(b, 'big')
n.to_bytes((n.bit_length()+7)//8, 'big')
B. 常用 Linux 命令
# 查找文件
find . -name "*.txt" -type f
# 搜索文件内容
grep -r "flag{" .
# 监控进程
top / htop
# 网络工具
nc -lvnp 4444 # 监听端口
curl -X POST -d "data" url
C. macOS 适配
# 用 Homebrew 替代 apt
brew install git python3 node docker
# Colima 替代 Docker Desktop
brew install colima
colima start --cpu 4 --memory 8
# pip 加 --user
pip3 install --user sympy pwntools
结语
CTF 的核心不是记住多少工具,而是面对未知时能否快速建立分析框架。本文档提供了"环境 → 工具 → 方法 → 实战"的完整链条,但真正的能力来自于不断做题、踩坑、总结。
记住几个原则:
- 信息收集 > 盲目尝试 — 先搞清楚题目给了什么
- 从简单到复杂 — 先排查明显的弱点
- 验证比解密更重要 — 解出结果后务必正向验证
- 善用搜索引擎 — 遇到新题型先搜 writeup 学习思路
祝你在赛场上旗开得胜!
免责声明:本文档为CTF竞赛技术学习资料,所有攻击技术、工具使用方法及漏洞利用代码,均仅供在合法授权的CTF竞赛环境或明确授权的渗透测试中使用。未经授权的系统入侵、数据访问或破坏行为将触犯法律。作者不承担任何因不当使用本文技术而产生的法律责任。请合理使用,共建安全的网络环境。
浙公网安备 33010602011771号