模块:Attacking Web Applications with Ffuf(13 小节)
核心工具:ffuf
目标:发现未公开的目录、页面、扩展名、子域、虚拟主机、参数和参数值。
边界:只对明确授权的域名、主机和 Web 应用进行模糊测试。

0. 本章工作流
确认目标可访问
→ 选取合适字典
→ 目录模糊测试
→ 识别技术栈和文件扩展名
→ 页面模糊测试
→ 递归发现深层路径
→ DNS / 子域 / vhost 枚举
→ 校准并过滤噪声
→ GET / POST 参数名枚举
→ 参数值枚举
→ 手工验证所有有效结果
前置知识
- Linux 命令行。
- HTTP 请求、响应和状态码。
- URL、Host Header、GET/POST 参数。
- DNS、子域与虚拟主机的基本区别。
1. Introduction
手工浏览只能看到页面主动公开的链接,而未链接的后台目录、旧文件、备份、开发接口和隐藏参数往往不会出现在前端。ffuf 将字典中的候选值替换到请求中的指定位置,并通过响应状态码、大小、字数、行数和耗时帮助识别有效结果。
本章主要覆盖:
- 目录模糊测试。
- 文件及扩展名模糊测试。
- 隐藏子域和虚拟主机。
- GET/POST 参数名。
- 参数值。
200 通常表示资源成功返回,但不能只依赖状态码;重定向、鉴权页和自定义 404 也可能返回不同或相同的状态码。
2. Web Fuzzing
2.1 首次使用与环境检查
课程环境通常已经安装 ffuf:
ffuf -h
ffuf -V
若系统仓库提供:
sudo apt install ffuf
本章常用 SecLists 字典:
ls /opt/useful/seclists/Discovery/Web-Content/
ls /usr/share/seclists/Discovery/Web-Content/
不同环境中 SecLists 路径可能不同,应先确认实际文件位置。
2.2 基本语法
ffuf -w <WORDLIST> -u 'http://<TARGET>/FUZZ'
FUZZ 是占位符。ffuf 逐行读取字典,把每个候选词替换到 FUZZ 所在位置。
2.3 常见参数
参数 作用 -w 指定字典,可给字典设置关键字 -u 目标 URL -H 增加请求 Header -X 指定 HTTP 方法 -d 请求体数据 -b Cookie -t 并发线程数 -rate 每秒请求上限 -timeout 请求超时 -r 跟随重定向 -v 显示完整 URL 等详细信息 -s 静默输出 -ac 自动校准过滤规则 -o / -of 输出文件及格式
首次测试应使用合理线程,并先请求一个不存在的随机路径,观察目标对 404 的真实处理方式。
3. Directory Fuzzing
3.1 基本目录枚举
ffuf -w /opt/useful/seclists/Discovery/Web-Content/directory-list-2.3-small.txt \
-u 'http://<TARGET>/FUZZ'
典型结果:
admin [Status: 301, Size: ..., Words: ..., Lines: ...]
uploads [Status: 301, Size: ..., Words: ..., Lines: ...]
目录经常返回 301/302 跳转到带 / 的路径;不要只显示 200。
3.2 带认证、Cookie 或自定义 Header
ffuf -w directories.txt -u 'http://<TARGET>/FUZZ' \
-H 'Authorization: Bearer <TOKEN>' \
-b 'session=<COOKIE>'
需要特定 Host:
ffuf -w directories.txt -u 'http://<IP>/FUZZ' \
-H 'Host: app.example.com'
3.3 判断结果
- 200:资源返回,但仍需确认不是统一错误页。
- 301/302/307/308:可能是目录标准化、登录跳转或另一页面。
- 401:资源存在,但需要身份验证。
- 403:资源可能存在,但当前身份无权访问。
- 404:通常不存在,也可能是应用故意伪装。
- 500:输入触发服务端错误,应谨慎复核。
4. Page Fuzzing
目录存在后,需要识别该应用使用的文件扩展名,再枚举页面。
4.1 扩展名模糊测试
ffuf -w /opt/useful/seclists/Discovery/Web-Content/web-extensions.txt \
-u 'http://<TARGET>/indexFUZZ'
如果结果显示 .php 有效,继续:
ffuf -w /opt/useful/seclists/Discovery/Web-Content/directory-list-2.3-small.txt \
-u 'http://<TARGET>/FUZZ.php'
4.2 多扩展名
ffuf -w pages.txt:PAGE -w extensions.txt:EXT \
-u 'http://<TARGET>/PAGE.EXT'
给字典命名后,URL 中使用对应关键字,而不是固定 FUZZ。
4.3 常见目标
.php .asp .aspx .jsp .html .txt .xml .json
.bak .old .orig .zip .tar.gz
字典应与技术栈匹配。若服务器明显使用 PHP,盲目枚举大量 Java 或 ASP.NET 扩展只会增加噪声。
5. Recursive Fuzzing
单层目录枚举可能发现 /admin,但更深层的 /admin/panel 仍不可见。ffuf 可对发现的目录继续递归。
ffuf -w directories.txt -u 'http://<TARGET>/FUZZ' \
-recursion -recursion-depth 2
结合扩展名:
ffuf -w directories.txt -u 'http://<TARGET>/FUZZ' \
-recursion -recursion-depth 2 -e .php,.html
5.1 参数意义
- -recursion:对匹配到的目录继续 fuzz。
- -recursion-depth:限制最大深度,避免无限扩大请求量。
- -e:在候选后追加扩展名。
5.2 风险控制
递归会显著增加请求量。先使用小字典和较浅深度,确认过滤规则准确,再扩大范围。
6. DNS Records
子域和虚拟主机不是同一概念:
- DNS 子域:DNS 中存在记录,通常解析为 IP。
- 虚拟主机:Web 服务器依据 HTTP Host Header 选择站点,可能没有公开 DNS 记录。
开始枚举前先确定根域及 DNS 解析:
dig example.com
nslookup example.com
host example.com
如果实验目标只提供 IP,需要把域名写入 /etc/hosts:
<TARGET_IP> example.com
修改后验证:
curl -I http://example.com/
DNS 解析失败时,子域 fuzz 的网络行为和 vhost fuzz 的 Host Header 行为会不同,应分别处理。
7. Sub-domain Fuzzing
子域枚举把 FUZZ 放在域名标签中:
ffuf -w /opt/useful/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-u 'http://FUZZ.example.com/'
7.1 前置条件
- 根域名可被 DNS 解析。
- DNS 能返回候选子域的地址。
- 环境不存在把任意子域都解析到同一地址的通配符记录,或已经建立对应过滤。
7.2 通配符 DNS
先查询随机子域:
dig random-name-that-should-not-exist.example.com
若仍返回 IP,说明存在 wildcard DNS。此时需要根据响应内容、IP 或其他特征过滤,不能把所有结果视为有效子域。
8. Vhost Fuzzing
Vhost 枚举把 FUZZ 放入 Host Header,并直接访问已知服务器 IP:
ffuf -w /opt/useful/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-u 'http://<TARGET_IP>/' \
-H 'Host: FUZZ.example.com'
8.1 为什么要用 IP
候选 vhost 可能没有公开 DNS 记录。直接连接 IP,再修改 Host,由 Web 服务器决定返回哪个站点。
8.2 验证结果
找到 admin.example.com 后加入 /etc/hosts:
<TARGET_IP> admin.example.com
随后浏览器或 curl 验证:
curl -I http://admin.example.com/
vhost fuzz 常遇到所有 Host 都返回默认站点,因此通常需要按响应大小过滤。
9. Filtering Results
过滤是本章核心。匹配参数决定“保留什么”,过滤参数决定“排除什么”。
9.1 Match 参数
参数 含义 -mc 匹配状态码 -ms 匹配响应大小 -mw 匹配单词数 -ml 匹配行数 -mr 匹配正则表达式 -mt 匹配响应时间
9.2 Filter 参数
参数 含义 -fc 过滤状态码 -fs 过滤响应大小 -fw 过滤单词数 -fl 过滤行数 -fr 过滤正则表达式 -ft 过滤响应时间
9.3 典型用法
过滤统一错误页大小:
ffuf -w words.txt -u 'http://<TARGET>/FUZZ' -fs 900
只显示指定状态码:
ffuf -w words.txt -u 'http://<TARGET>/FUZZ' -mc 200,204,301,302,307,401,403
自动校准:
ffuf -w words.txt -u 'http://<TARGET>/FUZZ' -ac
9.4 正确校准步骤
- 请求随机、肯定不存在的路径或 Host。
- 记录状态码、Size、Words、Lines 和响应关键字。
- 选择最稳定的特征过滤。
- 对保留下来的结果逐一手工请求。
仅按状态码过滤通常不够,因为许多应用为所有请求返回 200 自定义错误页。
10. Parameter Fuzzing - GET
目标是发现页面接受但前端未公开的 GET 参数名。
10.1 参数名枚举
ffuf -w /opt/useful/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-u 'http://<TARGET>/page.php?FUZZ=test' \
-fs <BASELINE_SIZE>
FUZZ 位于参数名位置,固定测试值用于观察不同参数是否改变响应。
10.2 带已有参数
ffuf -w parameters.txt \
-u 'http://<TARGET>/page.php?id=1&FUZZ=test'
10.3 结果验证
发现参数后使用 curl 或浏览器手工请求:
curl 'http://<TARGET>/page.php?debug=test'
响应大小不同可能只是错误提示;必须确认参数确实影响业务、输出或服务器行为。
11. Parameter Fuzzing - POST
POST 参数位于请求体中,需要同时指定方法、数据和 Content-Type。
11.1 表单编码
ffuf -w parameters.txt \
-u 'http://<TARGET>/page.php' \
-X POST \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'FUZZ=test' \
-fs <BASELINE_SIZE>
11.2 JSON 请求
ffuf -w parameters.txt \
-u 'http://<TARGET>/api/endpoint' \
-X POST \
-H 'Content-Type: application/json' \
-d '{"FUZZ":"test"}'
11.3 会话与 CSRF
ffuf -w parameters.txt -u 'http://<TARGET>/page.php' -X POST \
-b 'PHPSESSID=<SESSION>' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'csrf=<TOKEN>&FUZZ=test'
若 CSRF Token 每次请求都会变化,静态 ffuf 请求可能全部失败,需要先理解应用会话机制或使用其他自动化方式维护 Token。
12. Value Fuzzing
参数名已知后,把 FUZZ 放在参数值位置。
12.1 GET 值枚举
ffuf -w values.txt \
-u 'http://<TARGET>/page.php?id=FUZZ' \
-fs <NOT_FOUND_SIZE>
数字范围:
seq 1 1000 > ids.txt
ffuf -w ids.txt -u 'http://<TARGET>/page.php?id=FUZZ'
也可通过命令输入:
ffuf -w <(seq 1 1000) -u 'http://<TARGET>/page.php?id=FUZZ'
12.2 POST 值枚举
ffuf -w values.txt -u 'http://<TARGET>/page.php' -X POST \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'id=FUZZ' \
-fs <BASELINE_SIZE>
12.3 判断有效值
- Size/Words/Lines 与无效基线不同。
- 状态码或重定向位置改变。
- 响应中出现实体名称、数据字段或成功信息。
- 响应时间有稳定差异。
枚举对象 ID 时必须注意越权风险。发现其他对象可访问后,先最小化验证,不应批量下载不属于测试目标的数据。
13. Skills Assessment - Web Fuzzing
13.1 推荐解题顺序
- 用 curl 检查目标、状态码和默认响应。
- 目录 fuzz,记录 200/3xx/401/403。
- 针对有效目录识别扩展名与页面。
- 开启浅层递归寻找深层资源。
- 确认域名后分别执行子域与 vhost fuzz。
- 对统一错误页建立 -fs/-fw/-fl/-fr 过滤。
- 在目标页面 fuzz GET 参数名。
- 根据表单或请求格式 fuzz POST 参数名。
- 对有效参数 fuzz 值。
- 用 curl/浏览器逐条验证并整理攻击链。
13.2 综合命令模板
目录:
ffuf -w <DIR_WORDLIST> -u 'http://<HOST>/FUZZ' -ac
页面与扩展名:
ffuf -w <PAGE_WORDLIST> -u 'http://<HOST>/<PATH>/FUZZ.php' -ac
Vhost:
ffuf -w <SUBDOMAIN_WORDLIST> -u 'http://<IP>/' \
-H 'Host: FUZZ.<DOMAIN>' -fs <DEFAULT_SIZE>
GET 参数:
ffuf -w <PARAM_WORDLIST> \
-u 'http://<HOST>/<PAGE>?FUZZ=test' -fs <BASELINE_SIZE>
POST 参数:
ffuf -w <PARAM_WORDLIST> -u 'http://<HOST>/<PAGE>' -X POST \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'FUZZ=test' -fs <BASELINE_SIZE>
值:
ffuf -w <VALUE_WORDLIST> \
-u 'http://<HOST>/<PAGE>?<PARAM>=FUZZ' -fs <INVALID_SIZE>
附录 A:FUZZ 放置位置速查
测试目标 示例 目录 <http://host/FUZZ/> 页面名 <http://host/FUZZ.php> 扩展名 <http://host/indexFUZZ> 子域 <http://FUZZ.example.com/> Vhost -H 'Host: FUZZ.example.com' GET 参数名 ?FUZZ=test GET 参数值 ?id=FUZZ POST 参数名 -d 'FUZZ=test' POST 参数值 -d 'id=FUZZ' Cookie 值 -H 'Cookie: role=FUZZ' Header 值 -H 'X-Forwarded-For: FUZZ'
附录 B:输出与复现
保存 JSON 结果:
ffuf -w words.txt -u 'http://<TARGET>/FUZZ' \
-o results.json -of json
保存 HTML 报告:
ffuf -w words.txt -u 'http://<TARGET>/FUZZ' \
-o results.html -of html
通过 Web 代理观察请求:
ffuf -w words.txt -u 'http://<TARGET>/FUZZ' \
-x http://127.0.0.1:8080
代理模式适合调试小字典,不适合把高并发、大规模 fuzz 全部塞入 Burp/ZAP。
附录 C:常见失败原因
现象 原因与处理 所有候选都显示有效 目标使用统一错误页;建立 -fs/-fw/-fl/-fr 过滤 没有任何结果 URL、Host、DNS、协议或字典路径错误;检查基准请求 vhost 全部相同 返回默认站点;过滤默认响应大小 子域 fuzz 失败但 vhost 成功 候选无公开 DNS,但 Host Header 存在虚拟站点 POST 全部失败 缺少 Content-Type、Cookie、CSRF Token 或必要固定字段 页面返回 403 资源可能存在;不要直接当作不存在 301 结果被忽略 目录常跳转到带斜杠路径;保留 3xx 并手工验证 请求过多或超时 降低 -t、设置 -rate、缩小字典和递归深度 结果随时间波动 动态页面导致 Size 不稳定;改用 Words、Lines 或正则过滤 只找到目录未找到页面 继续做扩展名、页面和递归 fuzz
附录 D:测试记录模板
目标 URL / IP:
授权范围:
字典与来源:
FUZZ 位置:
基准错误响应:Status / Size / Words / Lines
匹配参数:
过滤参数:
线程与速率:
发现结果:
手工验证命令:
验证后的真实资源/参数:
敏感数据与权限影响:
总结
ffuf 的关键不是“跑一个大字典”,而是把 FUZZ 放在正确的请求位置,并用稳定的响应特征过滤噪声。实际工作中应遵循:先建立基线,再选择字典;先做浅层发现,再递归深入;自动化找到候选,手工请求完成验证。
