通用做题工作流
通用做题工作流
本文适合
刚开始做 CTF,或者经常在题目里乱试工具、难以复盘判断依据的学习者。学完你能:用"观察 -> 假设 -> 最小验证 -> 利用 -> 复盘"闭环处理一题,并把每一步写成可复现证据。
一句话判断
CTF 做题不是试工具列表,而是持续缩小不确定性;每一步都要能回答"我看到了什么、我猜什么、我用什么证据验证、下一步去哪"。
如果一题做完后只能写出最终 payload,却说不清从哪个信号走到这个 payload,说明工作流还没有闭环。
核心闭环可以先看成这张图:
题目中常见信号
第一判断:Web 或 AI 应用
起手动作:保存正常请求,列 URL、Cookie、Header、Body
第一判断:逆向或 Pwn
起手动作:file、运行样例、strings、保护检查
第一判断:Pwn、Crypto oracle 或交互题
起手动作:记录菜单、输入长度、回显和异常
第一判断:Misc
起手动作:file、xxd、strings、文件尾和嵌入扫描
第一判断:Crypto
起手动作:列参数、判断算法、估算爆破空间
第一判断:AI 安全
起手动作:画输入、模型、工具、输出边界
材料类型只决定第一批观察动作,不代表最终方向。Web 题可能藏 Go 二进制,Misc 附件可能转成 Crypto,AI 题也可能落到 Web 权限边界。
核心概念
含义:未修改输入时的正常表现
记录方式:状态码、输出、文件 hash、程序回显
含义:你能改变并再次提交的位置
记录方式:参数、文件、stdin、prompt、密文
含义:对异常现象的可验证解释
记录方式:"id 可能进入 SQL 查询"
含义:只改一个变量,让假设可证伪
记录方式:1=1/1=2、cyclic、重复 nonce 检查
含义:从验证成立走到 flag 的连续步骤
记录方式:泄露、计算、构造、提交
含义:验证失败后的下一步
记录方式:换输入点、换题型、补环境差异
工作流的价值不是把题目做慢,而是防止你在错误假设上越走越远。
最小分析流程
- 确认材料:URL、附件、源码、远程服务、模型或数据集分别列清。
- 保存基线:正常访问、正常运行、正常解压或正常推理的输出先留证据。
- 列可控输入:只写能实际改动的位置,不把猜测当入口。
- 提出 1 个假设:一次只验证一个方向,例如 SQL 注入、栈溢出、文件尾追加。
- 设计最小验证:明确预期输出和实际输出的差异。
- 判断分支:验证成功进入利用链,失败则记录原因并回到入口列表。
- 形成复现链:把最终步骤整理成"材料 -> 观察 -> 验证 -> 利用 -> flag"。
- 做复盘:把卡点写入 复盘不是订正答案 的模板。
最小记录模板:
入口:
基线:
我修改了:
预期:
实际:
判断:
下一步:最小验证示例
Web 参数假设
# 基线
curl -i 'http://target/item?id=1'
# 异常字符
curl -i "http://target/item?id=1'"
# 布尔差异
curl -i 'http://target/item?id=1 and 1=1'
curl -i 'http://target/item?id=1 and 1=2'判断依据:
基线返回正常内容 -> 参数有效
单引号返回数据库报错 -> 输入可能进入 SQL 语句
1=1 与 1=2 页面稳定不同 -> SQL 注入假设成立
下一步转 [[SQL注入入门]]二进制可控性假设
file ./chall
checksec ./chall
./chall
python - <<'PY'
from pwn import cyclic
print(cyclic(200).decode())
PY判断依据:
程序接收 stdin -> 存在可控输入
长输入导致崩溃 -> 有内存破坏现象
RIP/EIP 被 cyclic 覆盖 -> 可进入 [Pwn题的利用链思维](/ctf/方法论/Pwn题的利用链思维.html)
只崩溃但不能控制返回地址 -> 回退到输入长度、截断和保护机制排查常见利用 / 解题路线
路线总览:
利用阶段也要保持"一次只改一个变量"。拿到部分 flag、泄露地址、中间文件或模型输出后,都要重新观察,不要默认已经结束。
常见失败原因
可能原因:没保存基线,无法判断差异
排查动作:回到正常请求或正常运行输出
可能原因:同时改了多个变量
排查动作:固定其他参数,只改一个字段
可能原因:环境、版本、路径或交互差异
排查动作:记录远程完整输入输出,逐项对比
可能原因:没有证据就换方向
排查动作:每个假设至少留下成功或失败证据
可能原因:做题过程没有记录
排查动作:用"入口/基线/修改/判断/下一步"补流水
可能原因:复盘没有变成动作
排查动作:更新检查清单或知识条目
迷你案例
题目给一个 URL:/product?id=1。页面能正常显示商品详情。
第一步保存基线:id=1 返回商品 A,id=2 返回商品 B。第二步只改一个变量,提交 id=1',页面出现数据库错误。第三步提交 id=1 and 1=1 与 id=1 and 1=2,两者页面稳定不同。
此时不是直接乱跑所有 payload,而是把证据写成:
入口:GET /product?id=
基线:id=1/id=2 均返回商品详情
验证:单引号报错,布尔条件影响页面
判断:SQL 注入成立
下一步:列数、回显位、表名、flag 字段如果 id 测试完全无差异,就回到入口列表,继续检查 Cookie、Header、搜索框、上传点或 JS 暴露的 API,而不是在同一个参数上消耗时间。