夜雨聆风学习资料网

ARTICLE · 1233646

你下的安装包,竟是赏金局

你下的安装包,竟是赏金局

【文章说明】

  • 目的:本文内容仅为网络安全技术研究与教育目的而创作。

  • 红线:严禁将本文知识用于任何未授权的非法活动。使用者必须遵守《网络安全法》等相关法律。

  • 责任:任何对本文技术的滥用所引发的后果自负,与本公众号及作者无关。

  • 免责:内容仅供参考,作者不对其准确性、完整性作任何担保。

阅读即代表您同意以上条款。 

【内容描述】 系列终篇。前面两篇我们打的是"单点投毒",这篇把投毒升级成组合拳:用 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 deniedGET /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++ 里记录的请求。最常见的俩原因:

  1. Param Miner 开了"Add fcbz cachebuster"——会给 Burp 的请求加个静态查询参数。
  2. 服务器把 Accept-Encoding 头算进了缓存键——Burp 里"Proxy > Options > Remove unsupported encodings"会重写这个头。

两种情况,都只要调整你的投毒请求、确保缓存键对得上,就能解决。

八、防御:从最稳到最务实

最稳的防御,是关掉缓存。这对有些人不现实,但我怀疑不少网站是冲着 DDoS 防护或顺手拿 SSL 才接了 Cloudflare 之类,结果缓存默认开着,平白无故变脆弱。

其他有效手段:

  • 把缓存限制在纯静态响应,前提是你对"静态"的定义够警惕。
  • 别从请求头和 Cookie 取输入——这是治本,但你很难知道其他层、其他框架是不是偷偷支持了额外请求头。所以建议用 Param Miner 把每个页面都审计一遍,清掉未键控输入。
  • 识别出未键控输入后,理想是直接禁用;做不到就让缓存层剥离它,或把它加进缓存键。有些缓存支持用 Vary 头键控,有些允许自定义缓存键(但可能只给"企业"客户)。
  • 最后,哪怕你的应用没缓存,你的一些用户自己那端也可能有缓存,所以像 HTTP 头里的 XSS 这类客户端漏洞,绝不该被忽视。

九、结论:臃肿,正在合谋把威胁送到每个人面前

Web 缓存投毒远不是什么理论漏洞。臃肿的应用、高耸的服务器栈,正合起伙来把它送到每个人面前。我们看到了:连知名的框架都可能藏着危险到离谱的"通用功能",再次证明——绝不能因为某个项目开源、用户几百万,就默认"别人肯定把源码看过了"。我们也看到,在网站前面摆一道缓存,能把它从"完全安全"瞬间变成"严重脆弱"。

我觉得这是个大趋势的一部分:网站越来越嵌套进各种辅助系统里,其安全态势,也越来越难被孤立地充分评估。


📌系列三篇到此收官。要是这一路看下来觉得"缓存投毒真香",那就:

👍点个赞—— 三连支持,让更多白帽少走弯路

⭐关注我—— 更多一线漏洞研究、工具复盘持续输出

🔁转给团队 / 安全群—— 顺手帮同事排个雷

网络安全没有终点,下个坑见。🛡️

相关学习资料