缓存被嵌入到整个网络的网站中,在用户之间谨慎地处理数据,但它们却很少被深入审核。在本文中,我将向你展示如何远程探测缓存的内部工作原理,并使用gadget将其中的不一致性串联起来,构建漏洞利用链。Gadget 框架提出了一套标准API,在底层,USB 设备控制器 (USB Device Controller, UDC) 驱动则实现这一套 API。
这些漏洞遍布于缓存的各个层,从大量的CDN到缓存Web服务器和框架,一直到片段级内部模板缓存。本文我将演示如何构建漏洞利用链,并以禁用全球Firefox更新为例来进行详细介绍。
前言
缓存保存响应的副本,以减少后端系统上的载荷。当缓存收到HTTP请求时,它将计算请求的缓存密钥,并使用该密钥来确定是否已经保存了适当的响应,或者是否需要将请求转发到后端。缓存密钥通常由请求方法、路径、查询字符串和主机标头以及可能还有一个或两个其他标头组成。在以下请求中,未包含在高速缓存键中的值被涂成橙色。在整个演示过程中,我们将遵循这一突出显示的标准。
缓存密钥中未包含的请求组件称为“非密钥”组件。如果可以使用未加密的组件使应用程序提供有害的响应,则可以操纵缓存以保存该缓存并将其提供给其他用户:
在2018年,我发布了《实用Web缓存攻击》,其中展示了如何使用非标准的HTTP标头(例如X-Forwarded-Host和X-Original-URL)来攻击缓存并破坏网站。这是一种简单的方法,它利用了缓存中的设计缺陷,因此对所有缓存的影响均等。
在本文中,我将针对几乎总是包含在缓存密钥中的两个请求组件,主机标头和请求行。如果这些组件被直接放置到缓存密钥中,就不可能使用它们进行缓存攻击。但是,通过仔细检查,我们会发现这些值通常会被解析,转换和规范化,从而引入漏洞,让我们可以利用漏洞。这些差距源于危险但刻意设计的功能,一直到解析错误和天真地逃避漏洞,这些漏洞让我们产生完全不同的请求冲突。
方法
为了可靠地识别这类缓存攻击漏洞,我们将采用以下方法:
选择一个Cache Oracle
我们感兴趣的实现和配置因站点的不同而有所不同,因此从理解目标缓存的工作方式开始是至关重要的。为了实现这一点,我们需要在目标站点上选择一个端点,我将其称为Cache Oracle。这个端点必须是可缓存的,并且必须有一些方法来告诉你是否缓存命中或未命中。这可以是一个显式的HTTP标头,如CF-Cache-Status: HIT,或者可以通过动态内容或响应时间来推断。
理想情况下,Cache Oracle还应该反映整个URL和至少一个查询参数。这将帮助我们找到缓存参数解析和应用程序参数解析之间的差异,稍后我会详细介绍。
如果你非常幸运,并且你很好地询问了oracle,它会告诉你缓存密钥。或者至少给你三个冲突的缓存密钥,每个密钥都有一个真实的元素:
密钥处理
选择Cache Oracle之后,下一步是向它询问一系列漏洞,以确定在保存到缓存密钥时请求是否以任何方式进行了转换。常见的可利用转换包括删除特定查询参数、删除整个查询字符串、删除主机报标头中的端口以及url解码。
通过发出两个略有不同的请求并观察第二个请求是否引起缓存命中来询问每个漏洞,这表明它是与第一个发出相同的缓存键。
下面是一个改编自真实网站的简单例子,对于我们的Cache Oracle,我们将使用目标的主页,因为它反映了主机标头,并有一个响应标头,告诉我们是否命中了缓存:
首先,如下所示:
然后,我们删除端口,重播请求,看看我们是否得到缓存命中:
看来我们做到了,除了确认该站点的缓存密钥中未包含端口之外,我们还永久删除了其主页,任何试图访问该主页的人都会被重定向到一个失效的端口,从而导致超时。我发现此缓存密钥漏洞存在于许多CDN上,尤其是Cloudflare和Fastly。并且现在Fastly现在已经对漏洞进行了修复,但Cloudflare还没有修复。
许多缓存密钥漏洞直接支持了像这样的单请求DoS攻击,并且可能会诱使按原样报告它们并继续前进。
漏洞利用
将我们的缓存密钥转换成为高影响力的漏洞的最后一步是在目标网站上找到一个gadget,以与我们的转换建立链接。gadget被反射,客户端行为,如XSS、打开重定向,以及其他没有分类的行为,因为它们通常是无害的。缓存攻击可以通过gadget实现以下三种主要方式:
1.通过将“XSS”这样的漏洞“存储”起来,利用浏览过的攻击页面的每个人,来提高其严重性;
2.启用对资源文件(例如JS和CSS)中动态内容的利用;
3.启用利用依赖于浏览器不会发送的格式错误的请求的“无法利用的”漏洞;
这三种情况中的每一种都可能导致整个站点的接管,至关重要的是,后两种行为通常被认为是无法利用的,因此无法修补。
案例研究
让我们看看当我们把这个方法应用到真实的网站上会发生什么,在本节中,我将探索一系列缓存密钥处理漏洞,这些漏洞来自于带有漏洞奖励程序的网站,并分享一些可以与之结合的gadget。
未加密的查询检测
最常见的缓存密钥转换是从密钥中删除整个查询字符串,我敢肯定,在我之前就有人意识到并利用了这种行为,但它肯定没有得到应有的重视。与直觉相反,它也很难被发现,因为它将动态页面伪装成静态页面。不过你可以通过更改参数的值并观察响应中的某种差异来轻松识别大多数动态页面:
但是,当查询字符串被从缓存密钥中删除时,这种方法不起作用。在这种情况下,即使添加一个额外的cache-buster参数也没有任何效果:
除非页面清楚地表明何时缓存命中,否则很容易将此页面作为静态写入,继续运行并删除一个密钥漏洞。对于漏洞扫描程序而言,情况更糟,它们将盲目发送有效载荷,而后再不经过缓存。这不仅会影响查询参数中的漏洞,甚至意味着像我的Param Miner这样的工具将无法检测到未输入的标头。Param Miner扩展可以标识隐藏的,未链接的参数。这对于查找Web缓存中毒漏洞特别有用。
它结合了Backslash Powered Scanner的高级差异逻辑和二进制搜索技术,每个请求最多猜测65000个参数名称。参数名称来自精心策划的内置词表,它还从所有范围内的流量中获取其他词。
要使用它,请在Burp中右键单击一个请求,然后单击“ Guess(cookies | headers | params)”。如果你使用的是Burp Suite Pro,识别出的参数将报告为扫描仪漏洞。如果没有,你可以在Extender->Extensions->Param Miner->Output下找到它们。你还可以同时对多个选定请求发起猜测攻击。这将使用线程池,因此你可以根据需要在数千个请求中安全地使用它。另外,你可以启用所有范围内流量的自动挖掘。请注意,此工具旨在具有高度的可扩展性,但可能需要进行调整以避免性能漏洞。
那么,我们如何才能突破这种缓存行为并打入后端系统呢?一种方法是将缓存破坏器放在可以安全地编辑而没有明显副作用的任何标头中,并且可能包含在缓存密钥中:
这种方法在某些系统上非常有效,例如,运行Cloudflare的站点默认在缓存密钥中包含源。我已经更新了Param Miner,让它在默认情况下将这项技术应用到自己的请求上,你可以通过选择‘Add static cachebuster’和‘Include cachebusters in headers’来为所有Burp套件流量启用它。然而,这种方法并不完美。不过有些网站的缓存密钥中没有包含这些标头,而在其他网站,我们的缓存终结程序可能会破坏这些内容。
幸运的是,我们还有其他一些可能的选择。在某些目标上,你会发现可以使用HTTP方法PURGE和FASTLYPURGE直接从目标的高速缓存中删除条目,而无需进行身份验证。这对于实时缓存攻击非常有用,而且当缓存破坏者失败时,它也可以方便地穿透缓存。但是,在明显的竞争条件和对站点其他用户的攻击之间,不适合进行自动化。
我们还可以尝试最后一种方法,缓存从缓存密钥中删除路径的情况非常罕见,根据后端系统的不同,我们可以利用路径标准化来发出具有不同密钥但仍然到达相同端点的请求。这里有四种不同的方法在不同的系统上敲路径'/':
无加密的查询利用
如果你在野外应用这些缓存破坏技术,你可能会发现自己受到一些极其“明显”的漏洞的奖励。例如,在一份在线报纸上,我发现每一页的查询反射都会导致XSS:
通常情况下,像这样的漏洞在一个漏洞奖励程序的站点上不会持续五分钟,但是因为查询字符串没有包含在缓存密钥中,缓存就会对其他所有人隐藏。
缓存配置不仅屏蔽了XSS,这也使得情况更加严重。由于XSS有效载荷不在缓存密钥中,任何随后命中相同路径的人都将收到我们的攻击响应:
实际上,我可以完全控制网站上的每个页面,包括主页。
路径中的second /在我之前的研究中扮演了'dontpoisoneveryone'参数的角色,它确保了在复制这个漏洞时,我们不会影响真正的访问者。发起真正的攻击只是删除多余的斜杠,或者正确定时请求或使用PURGE方法。
重定向拒绝服务
如果你发现一个未键入查询字符串的站点,但是没有一个方便的、被忽视的XSS漏洞,该怎么办?关于网络缓存攻击,我最喜欢做的事情之一就是利用缓存供应商的网站,所以让我们使用www.cloudflare.com来回答这个漏洞。
Cloudflare的登录页面位于dash.cloudflare.com/login,但有不少链接定向到cloudflare.com/login,它通过/login/重定向用户。使用这个重定向作为我们的Cache Oracle,我们可以快速确认他们删除了查询字符串从缓存密钥:
这种重定向看起来可能没有太大的利用潜力,但缓存攻击几乎可以把任何东西变成DoS攻击。如果我们将查询字符串填充到最大请求URI长度:
然后,当其他人尝试访问登录页面时,他们自然会获得带有长查询字符串的重定向:
当他们的浏览器遵循此规则时,多余的正斜杠会使URI延长一字节,从而导致URI被服务器阻止:
因此,仅需一个请求,我们就可以永久删除通往Cloudflare登录页面的路由。这全都归功于重定向,我们无法通过自己发送超长URI来进行此攻击,因为Cloudflare拒绝缓存任何带有错误状态代码(如414)的响应。重定向添加了一层间接层,使这种攻击成为可能。同样,即使dash.cloudflare.com/login上的登录页面不可缓存,我们仍然可以使用缓存攻击来通过重定向向其添加恶意参数。
通常,如果你发现一个正在积极使用并反映查询参数的可缓存重定向,你可以在重定向目标上注入参数,即使目标页不能缓存或在同一域中。
修补重定向
Cloudflare本可以通过调整自己网站上的重定向来轻松地修补这个漏洞,但这会让他们的许多客户受到攻击。相反,他们添加了一个全局缓解来禁用反映请求查询字符串的重定向缓存。不幸的是,我能够使用URL编码来绕过此操作:
现在已经解决了这种绕过漏洞,但是,如果你发现服务器在对查询进行任何其他转换之后再将其放置在Location标头中,则可以再次绕过缓解措施。
缓存参数隐藏(Cache Parameter Cloaking)
到目前为止,我们已经看到,当站点从缓存密钥中删除整个查询字符串时,可能会出现很多攻击。但是,如果一个站点只是简单地删除了一个特定的参数,例如utm_content等无害分析参数,会发生什么情况呢?理论上,这是不可利用的,只要该站点没有反映整个URL的gadget即可。
实际上,当一个网站试图从缓存密钥中删除一个特定的参数时,我们通常可以利用URL解析技巧哄骗它部分地删除密钥中的任意参数,我们将这种技术称为缓存参数隐藏。
让我们从一个攻击目标开始,从StackOverflow提取的Varnish正则表达式,该正则表达式旨在删除参数'_':
鉴于此正则表达式以及以下请求:
我们可以在不更改缓存秘钥的情况下攻击参数'q',如下所示:
注意,因为正则表达式会留下?在缓存密钥中,我们只能破坏包含问号的参数。像这样的替换通常会对攻击造成各种奇怪和不同的限制。
在下一篇文章中,我们将介绍Akamai,Ruby on Rails,使用Fat GET请求等攻击过程。
本文翻译自:https://portswigger.net/research/web-cache-entanglement如若转载,请注明原文地址: