免责声明:本文档为CTF(Capture The Flag)网络安全竞赛技术学习资料,旨在帮助安全从业者系统提升各方向手工解题能力。文中涉及的所有技术方法、工具使用及攻击手法,仅适用于合法授权的CTF竞赛及安全测试场景。严禁用于任何未经授权的系统入侵、数据窃取或破坏行为。请严格遵守《中华人民共和国网络安全法》及相关法律法规,合理使用安全技术。

CTF 比赛环境搭建与手工解题实战指南

本文档覆盖 环境搭建 + 全方向手工解题流程,以 Ubuntu 22.04 为基准环境,附 macOS 适配说明。文中穿插三道实战题目的完整解题过程,确保"看完就能动手"。


目录


第一部分:环境搭建

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)

排查过程

  1. e = 3 是危险信号,n 只有 314 bit 也是
  2. FactorDB 分解 n,发现 三个素数
  3. 尝试标准 RSA 解密:inverse(3, phi) 报错,因为 gcd(3, phi) = 3
  4. 改用 立方根 + CRTsympy.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

Flag0ctf{HahA!Thi5_1s_n0T_rSa~}

3.3 实战:Fermat 分解法秒破 4096-bit RSA

题目:某 CTF RSA 题 (4096-bit, e=65537)

排查过程

  1. e = 65537,n = 4096 bit,看起来标准
  2. Wiener 攻击失败(d 不够小)
  3. 检查 sqrt(n) 附近,发现 (sqrt(n)+1)^2 - n 很小
  4. 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))

Flagflag{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)

分析过程

  1. strings 发现 PDB 路径 c++_rc4.pdb → 确认是 RC4
  2. 提取关键字符串:密钥 12345678abcdefghijklmnopqrspxyzerrornice job
  3. 用 Capstone 搜索引用这些字符串的 lea 指令,定位 main 函数
  4. 反汇编发现加密流程:
    • RC4_KSA(key, 32) 初始化 S-box
    • RC4_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 的核心不是记住多少工具,而是面对未知时能否快速建立分析框架。本文档提供了"环境 → 工具 → 方法 → 实战"的完整链条,但真正的能力来自于不断做题、踩坑、总结。

记住几个原则

  1. 信息收集 > 盲目尝试 — 先搞清楚题目给了什么
  2. 从简单到复杂 — 先排查明显的弱点
  3. 验证比解密更重要 — 解出结果后务必正向验证
  4. 善用搜索引擎 — 遇到新题型先搜 writeup 学习思路

祝你在赛场上旗开得胜!


免责声明:本文档为CTF竞赛技术学习资料,所有攻击技术、工具使用方法及漏洞利用代码,均仅供在合法授权的CTF竞赛环境或明确授权的渗透测试中使用。未经授权的系统入侵、数据访问或破坏行为将触犯法律。作者不承担任何因不当使用本文技术而产生的法律责任。请合理使用,共建安全的网络环境。