当前位置: 首页 > article >正文

【Academy】Web 缓存中毒 ------ Web cache poisoning

Web 缓存中毒 ------ Web cache poisoning

  • 1. 什么是 Web 缓存中毒?
  • 2. Web 缓存如何工作?
  • 3. Web 缓存中毒攻击的影响是什么?
  • 4. 构建 Web 缓存中毒攻击
    • 4.1 识别并评估未被用作缓存键的输入
    • 4.2 从后端服务器引出有害响应
    • 4.3 使响应被缓存
  • 5. 利用缓存设计缺陷
    • 5.1 使用 Web 缓存中毒进行 XSS 攻击
    • 5.2 使用 Web 缓存中毒来利用不安全的资源导入处理
    • 5.3 利用网页缓存投毒来利用 Cookie 处理漏洞。
    • 5.4 使用多个标头来利用 Web 缓存中毒漏洞
    • 5.5 利用暴露过多信息的响应
      • 5.5.1 缓存控制指令
      • 5.5.2 Vary 标头
    • 5.6 使用 Web 缓存中毒来利用基于 DOM 的漏洞
    • 5.7 链式 Web 缓存投毒漏洞
  • 6. 利用缓存实现缺陷
    • 6.1 缓存键缺陷
    • 6.2 缓存探测方法
      • 6.2.1 确定合适的缓存预言机(cache oracle)
      • 6.2.2 探测键处理
      • 6.2.3 识别可利用的小工具
    • 6.3 利用缓存键缺陷
      • 6.3.1 未被用作缓存键的端口
      • 6.3.2 未键查询字符串
      • 6.3.3 缓存参数隐藏
      • 6.3.4 规范化缓存键
      • 6.3.5 缓存键注入
      • 6.3.6 内部缓存投毒
  • 7. 如何防止 Web 缓存中毒漏洞


概述
在本文中,我们将讨论什么是 Web 缓存中毒以及哪些行为会导致 Web 缓存中毒漏洞。我们还将研究利用这些漏洞的一些方法,并提出减少暴露于这些漏洞的方法。


1. 什么是 Web 缓存中毒?

Web 缓存中毒是一种高级技术,攻击者利用 Web 服务器和缓存的行为,以便向其他用户提供有害的 HTTP 响应。

从根本上说,Web 缓存中毒涉及两个阶段。首先,攻击者必须弄清楚如何从后端服务器引出无意中包含某种危险负载的响应。一旦成功,他们需要确保他们的响应被缓存并随后提供给预期的受害者。

中毒的 Web 缓存可能是一种毁灭性的手段,可以分发多种不同的攻击,利用 XSS、JavaScript 注入、开放重定向等漏洞。


2. Web 缓存如何工作?

要了解 Web 缓存中毒漏洞是如何产生的,重要的是要对 Web 缓存的工作原理有一个基本的了解。

如果服务器必须单独向每个 HTTP 请求发送新的响应,这可能会使服务器过载,从而导致延迟问题和糟糕的用户体验,尤其是在繁忙时段。缓存主要是减少此类问题的一种方法。

缓存位于服务器和用户之间,它保存(缓存)对特定请求的响应,通常保留固定的时间。如果另一个用户随后发送等效请求,则缓存只需将缓存响应的副本直接提供给用户,而无需来自后端的任何交互。这通过减少服务器必须处理的重复请求数量,大大减轻了服务器的负载。

Normal cache behavior


缓存键
当缓存收到 HTTP 请求时,它首先必须确定是否有可以直接提供的缓存响应,或者是否必须转发请求以供后端服务器处理。缓存通过比较请求组件的预定义子集(统称为“缓存键”)来识别等效请求。通常,这将包含请求行和 Host 标头。缓存键中未包含的请求组件称为“unkeyed”。

如果传入请求的缓存键与前一个请求的键匹配,则缓存会将它们视为等效。因此,它将提供为原始请求生成的缓存响应的副本。这适用于具有匹配缓存键的所有后续请求,直到缓存的响应过期。

至关重要的是,缓存完全忽略了请求的其他组件。我们稍后将更详细地探讨此行为的影响。


3. Web 缓存中毒攻击的影响是什么?

Web 缓存中毒的影响在很大程度上取决于两个关键因素:

  • 攻击者究竟能成功缓存什么
    由于被污染的缓存更多的是一种分发手段而非独立攻击,因此 Web 缓存投毒的影响与注入的有效载荷的危害性密不可分。与大多数攻击类型一样,Web 缓存投毒也可以与其他攻击结合使用,以进一步扩大潜在影响。
  • 受影响页面的流量大小
    当缓存被污染时,被污染的响应只会提供给访问受影响页面的用户。因此,其影响可能从不存在到巨大不等,这取决于页面是否受欢迎。例如,如果攻击者成功地污染了一个主要网站首页的缓存响应,那么这次攻击可能会影响成千上万的用户,而攻击者无需进行任何后续交互。

请注意,缓存条目的持续时间不一定会影响 Web 缓存中毒的影响。通常,可以以这样一种方式编写攻击脚本,使其无限期地重新毒害缓存。


4. 构建 Web 缓存中毒攻击

一般来说,构建基本的 Web 缓存中毒攻击包括以下步骤:

  1. 识别并评估未被用作缓存键的输入
  2. 从后端服务器引出有害响应
  3. 使响应被缓存

4.1 识别并评估未被用作缓存键的输入

任何 Web 缓存中毒攻击都依赖于对无键输入(如标头)的操纵。在决定是否向用户提供缓存响应时,Web 缓存会忽略无键输入。这种行为意味着你可以使用它们注入有效载荷并引发“中毒”响应,如果被缓存,该响应将提供给所有请求具有匹配缓存键的用户。因此,构建 Web 缓存中毒攻击的第一步是识别服务器支持的无键输入。

你可以通过向请求中添加随机输入并观察它们是否对响应产生影响来手动识别无键输入。这可能很明显,例如直接在响应中反映输入,或者触发完全不同的响应。然而,有时影响更加微妙,需要一些侦探工作才能弄清楚。你可以使用诸如 Burp Comparer 之类的工具来比较有注入输入和没有注入输入的响应,但这仍然需要大量的手动工作。


Param Miner
幸运的是,您可以通过从 BApp 商店向 Burp 添加 Param Miner 扩展来自动化识别未键控输入的过程。要使用 Param Miner,您只需右键单击要调查的请求,然后单击“Guess headers”。然后,Param Miner 在后台运行,发送包含与其广泛的内置标头列表不同的输入的请求。如果包含其注入输入之一的请求对响应有影响,Param Miner 会将其记录在 Burp 中,如果您使用的是 Burp Suite Professional,则记录在“问题”窗格中,如果您使用的是 Burp Suite 社区版,则记录在扩展程序的“输出”选项卡中(“扩展”>“已安装”>“Param Miner”>“输出”)。

例如,在以下屏幕截图中,Param Miner 在网站主页上发现了一个未键的标头 X-Forwarded-Host

Param miner

注意:在对实时网站上的无键输入进行测试时,存在无意中导致缓存将你生成的响应提供给真实用户的风险。因此,确保你的请求都具有唯一的缓存键非常重要,这样它们才只会提供给你。为此,你可以在每次发出请求时手动向请求行添加一个缓存破坏器(例如一个唯一的参数)。或者,如果你正在使用参数挖掘器(Param Miner),则有选项可以自动为每个请求添加缓存破坏器。


4.2 从后端服务器引出有害响应

一旦你确定了一个无键输入,下一步就是准确评估网站如何处理它。理解这一点对于成功引发有害响应至关重要。如果输入在服务器的响应中被反映出来而没有经过适当的清理,或者被用于动态生成其他数据,那么这就是网络缓存中毒的一个潜在入口点


4.3 使响应被缓存

操纵输入以引发有害响应只是成功的一半,但除非你能使响应被缓存,否则这并没有多大作用,而这有时可能很棘手。
响应是否被缓存可能取决于各种因素,例如文件扩展名、内容类型、路由、状态码和响应头。你可能需要花一些时间在不同页面上试验请求并研究缓存的行为方式。一旦你弄清楚如何使包含恶意输入的响应被缓存,你就可以将漏洞利用传递给潜在受害者。

web cache poisoning


利用 Web 缓存中毒漏洞
这个基本过程可用于发现和利用各种不同的 Web 缓存中毒漏洞。
在某些情况下,Web 缓存中毒漏洞是由于缓存设计中的一般缺陷引起的。在其他时候,特定网站实现缓存的方式可能会引入可被利用的意外情况。
在以下部分中将概述这两种情况中最常见的一些示例。

  • 利用缓存设计缺陷
  • 利用缓存实现缺陷

5. 利用缓存设计缺陷

在本节中,我们将更仔细地研究 Web 缓存中毒漏洞是如何由于缓存设计中的一般缺陷而出现的。我们还将演示如何利用这些。

简而言之,如果网站以不安全的方式处理未键的输入并允许缓存后续的 HTTP 响应,则容易受到 Web 缓存中毒的影响。此漏洞可用作各种不同攻击的传递方法。


5.1 使用 Web 缓存中毒进行 XSS 攻击

也许最简单的 Web 缓存中毒漏洞是当未键输入反映在可缓存的响应中而没有进行适当的清理时。

例如,请考虑以下请求和响应:

GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: innocent-website.co.uk

HTTP/1.1 200 OK
Cache-Control: public
<meta property="og:image" content="https://innocent-website.co.uk/cms/social.png" />

在这里,X-Forwarded-Host 标头的值用于动态生成 Open Graph 图像 URL,然后将其反映在响应中。对于 Web 缓存中毒至关重要的是,X-Forwarded-Host 标头通常是未键的。在此示例中,缓存可能会因包含简单 XSS 有效负载的响应而中毒:

GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: a."><script>alert(1)</script>"

HTTP/1.1 200 OK
Cache-Control: public
<meta property="og:image" content="https://a."><script>alert(1)</script>"/cms/social.png" />

如果缓存了此响应,则将向访问 /en?region=uk 的所有用户提供此 XSS 有效负载。此示例只是导致受害者的浏览器中出现警报,但真正的攻击可能会窃取密码并劫持用户帐户。


5.2 使用 Web 缓存中毒来利用不安全的资源导入处理

一些网站使用未键的标头来动态生成用于导入资源(如外部托管的 JavaScript 文件)的 URL。在这种情况下,如果攻击者将相应标头的值更改为他们控制的域,则他们可能会纵 URL 以指向他们自己的恶意 JavaScript 文件。

如果缓存了包含此恶意 URL 的响应,则攻击者的 JavaScript 文件将被导入,并在其请求具有匹配缓存键的任何用户的浏览器会话中执行。

GET / HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: evil-user.net
User-Agent: Mozilla/5.0 Firefox/57.0

HTTP/1.1 200 OK
<script src="https://evil-user.net/static/analytics.js"></script>

5.3 利用网页缓存投毒来利用 Cookie 处理漏洞。

Cookie 通常用于在响应中动态生成内容。一个常见的示例可能是指示用户首选语言的 Cookie,然后使用它来加载页面的相应版本:

GET /blog/post.php?mobile=1 HTTP/1.1
Host: innocent-website.com
User-Agent: Mozilla/5.0 Firefox/57.0
Cookie: language=pl;
Connection: close

在此示例中,请求博客文章的波兰语版本。请注意,有关要提供的语言版本的信息仅包含在 Cookie 标头中。假设缓存键包含请求行和 Host 标头,但不包含 Cookie 标头。 在这种情况下,如果缓存了对此请求的响应,则所有尝试访问此博客文章的后续用户也将收到波兰语版本,无论他们实际选择了哪种语言。

缓存对 Cookie 的这种有缺陷的处理方式也可以使用 Web 缓存投毒技术进行利用。然而,在实际中,与基于标头的缓存投毒相比,这种方式相对较少见。当存在基于 Cookie 的缓存投毒漏洞时,它们往往会被快速识别和解决,因为合法用户可能会意外地使缓存中毒。


5.4 使用多个标头来利用 Web 缓存中毒漏洞

如上所述,某些网站容易受到简单的 Web 缓存中毒攻击。但是,其他方法需要更复杂的攻击,并且只有在攻击者能够构建作多个未键输入的请求时才会变得容易受到攻击。

例如,假设一个网站需要使用 HTTPS 进行安全通信。为了强制执行此作,如果收到使用其他协议的请求,网站会动态生成一个重定向到自身的重定向,该重定向使用 HTTPS:

GET /random HTTP/1.1
Host: innocent-site.com
X-Forwarded-Proto: http

HTTP/1.1 301 moved permanently
Location: https://innocent-site.com/random

就其本身而言,这种行为不一定容易受到攻击。但是,通过将其与我们之前了解的有关动态生成 URL 中的漏洞的信息相结合,攻击者可能会利用此行为生成可缓存的响应,将用户重定向到恶意 URL。


5.5 利用暴露过多信息的响应

有时,网站会泄露太多有关自身及其行为的信息,从而使自己更容易受到 Web 缓存中毒的影响。


5.5.1 缓存控制指令

构建网页缓存投毒攻击时的挑战之一是确保有害响应被缓存。这可能涉及大量的手动尝试和错误,以研究缓存的行为方式。然而,有时响应会明确揭示攻击者成功投毒缓存所需的一些信息。

一个这样的示例是,当响应包含有关清除缓存的频率或当前缓存的响应的存在时间的信息时:

HTTP/1.1 200 OK
Via: 1.1 varnish-v4
Age: 174
Cache-Control: public, max-age=1800

虽然这不会直接导致 Web 缓存中毒漏洞,但它确实为潜在攻击者节省了一些涉及的手动工作,因为他们确切地知道何时发送有效负载以确保它被缓存。

这种知识还能实现更为微妙的攻击。攻击者不必不断向后端服务器发送请求直到有一个请求成功(这样可能会引起怀疑),而是可以精心选择时机发送一个恶意请求来毒害缓存。


5.5.2 Vary 标头

Vary”标头的基本使用方式也常常为攻击者提供帮助。“Vary”标头指定了一个附加标头列表,即使这些标头通常未被用作键,也应将其视为缓存键的一部分。例如,它通常用于指定“User-Agent”标头是入键的,这样如果网站的移动版本被缓存,就不会错误地将其提供给非移动用户。

此信息还可用于构建针对特定用户子集的多步骤攻击。例如,如果攻击者知道 User-Agent 标头是缓存键的一部分,则通过首先识别预期受害者的用户代理,他们可以定制攻击,以便仅影响具有该用户代理的User-Agent。或者,他们可以找出最常用于访问该网站的用户User-Agent,并定制攻击以以这种方式影响最大用户数。


5.6 使用 Web 缓存中毒来利用基于 DOM 的漏洞

如前所述,如果网站不安全地使用未key的标头来导入文件,则攻击者可能会利用此漏洞来导入恶意文件。然而,这不仅适用于 JavaScript 文件。

许多网站使用JavaScript从后端获取和处理其他数据。如果脚本以不安全的方式处理来自服务器的数据,这可能会导致各种基于DOM的漏洞。

例如,攻击者可以使用导入包含以下有效负载的JSON文件的响应来毒害缓存:

{"someProperty" : "<svg onload=alert(1)>"}

如果网站随后将此属性的值传递到支持动态代码执行的接收器中,则有效负载将在受害者浏览器会话的上下文中执行。

如果您使用Web缓存中毒使网站从您的服务器加载恶意JSON数据,您可能需要使用CORS授予网站对JSON的访问权限:

HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: *

{
    "malicious json" : "malicious json"
}

5.7 链式 Web 缓存投毒漏洞

正如我们之前所见,有时攻击者只能通过使用多个标头精心制作请求来引发恶意响应。但对于不同类型的攻击也是如此。Web 缓存投毒有时需要攻击者将我们讨论过的几种技术串联起来。通过将不同的漏洞串联起来,通常可以暴露最初无法利用的额外漏洞层。


6. 利用缓存实现缺陷

在我们之前的研究中,学习了如何通过操纵典型的未键输入(如 HTTP 标头和 Cookie)来利用 Web 缓存中毒漏洞。虽然这种方法很有效,但它只是触及了 Web 缓存中毒可能的情况的皮毛。

在本节中,我们将演示如何通过利用缓存系统的特定实现中的特性来访问更大的 Web 缓存中毒攻击面。特别是,我们将研究为什么缓存键生成方式的缺陷有时会使网站容易受到传统上认为不可利用的单独漏洞的缓存中毒。我们还将展示如何进一步利用经典技术来毒害应用程序级缓存,这通常会带来毁灭性的后果。


6.1 缓存键缺陷

一般来说,网站的大部分输入来自 URL 路径和查询字符串。因此,这是各种黑客技术的常见攻击面。但是,由于请求行通常是缓存键的一部分,因此这些输入点传统上被认为不适合缓存中毒。通过键控输入注入(例如 HTTP 头和 cookies)的任何有效载荷都将起到缓存破坏者的作用,这意味着你投毒的缓存条目几乎肯定不会被提供给任何其他用户。

但是,仔细检查后,各个缓存系统的行为并不总是如您所料。在实践中,许多网站和内容分发网络(CDN)在将带键组件保存到缓存键中时会执行各种转换。这可能包括:

  • 排除查询字符串
  • 筛选出特定查询参数
  • 规范化键控组件中的输入

这些转换可能会带来一些意想不到的特性。这些主要基于写入缓存键的数据与传递到应用程序代码的数据之间的差异,即使它们都源于相同的输入。可以利用这些缓存键缺陷,通过最初可能看起来不可用的输入来毒害缓存。

对于完全集成的应用程序级缓存,这些特性可能更加极端。事实上,内部缓存可能非常不可预测,以至于有时很难在不无意中毒害实时用户的缓存的情况下对其进行测试。


6.2 缓存探测方法

探测缓存实现缺陷的方法与传统的 Web 缓存中毒方法略有不同。这些较新的技术依赖于缓存的特定实现和配置中的缺陷,这些缺陷可能因站点而异。这意味着您需要更深入地了解目标缓存及其行为。

在本节中,我们将概述探测缓存以了解其行为并识别任何潜在缺陷的高级方法。之后,我们将提供一些更具体的示例,说明常见的缓存键缺陷以及如何利用它们。

该方法包括以下步骤:

  1. 确定一个合适的缓存预言机
  2. 探测键处理
  3. 识别一个可利用的小工具

6.2.1 确定合适的缓存预言机(cache oracle)

第一步是确定一个合适的 “缓存预言机”,以便用于测试。缓存预言机只是一个页面或端点,它提供关于缓存行为的反馈。这个页面或端点必须是可缓存的,并且必须以某种方式表明你收到的是缓存响应还是直接来自服务器的响应。这种反馈可以有多种形式,例如:

  • 一个明确告诉你是否命中缓存的 HTTP 头
  • 动态内容的可观察变化
  • 不同的响应时间

理想情况下,缓存预言机还将在响应中反映整个 URL 以及至少一个查询参数。这将使你更容易注意到缓存和应用程序之间的解析差异,这对于以后构建不同的漏洞利用非常有用。

如果您可以确定正在使用特定的第三方缓存,您还可以查阅相应的文档。这可能包含有关如何构建默认缓存键的信息。您甚至可能会偶然发现一些方便的提示和技巧,例如允许您直接查看缓存键的功能。例如,基于 Akamai 的网站可能支持标头 Pragma: akamai-x-get-cache-key,您可以使用它在响应标头中显示缓存键:

GET /?param=1 HTTP/1.1
Host: innocent-website.com
Pragma: akamai-x-get-cache-key

HTTP/1.1 200 OK
X-Cache-Key: innocent-website.com/?param=1

6.2.2 探测键处理

下一步是调查缓存在生成缓存键时是否对输入执行任何其他处理。您正在寻找隐藏在看似键控的组件中的其他攻击面。

您应该专门查看正在发生的任何转变。当 keyed 组件被添加到缓存键中时,是否从键控组件中排除了任何内容?常见示例包括排除特定查询参数,甚至整个查询字符串,以及从 Host 标头中删除端口。

如果您有幸可以直接访问缓存键,则可以在注入不同的输入后简单地比较键。否则,您可以利用对高速缓存预言机的理解来推断您是否收到了正确的高速缓存响应。对于要测试的每个案例,您发送两个类似的请求并比较响应。

假设我们的缓存预言机是目标网站的主页。这会自动将用户重定向到特定地区的页面。它使用 Host 标头在响应中动态生成 Location 标头:

GET / HTTP/1.1
Host: vulnerable-website.com

HTTP/1.1 302 Moved Permanently
Location: https://vulnerable-website.com/en
Cache-Status: miss

要测试端口是否被排除在缓存键之外,我们首先需要请求一个任意端口,并确保我们从服务器收到反映此输入的新响应:

GET / HTTP/1.1
Host: vulnerable-website.com:1337

HTTP/1.1 302 Moved Permanently
Location: https://vulnerable-website.com:1337/en
Cache-Status: miss

接下来,我们将发送另一个请求,但这次我们不会指定端口:

GET / HTTP/1.1
Host: vulnerable-website.com

HTTP/1.1 302 Moved Permanently
Location: https://vulnerable-website.com:1337/en
Cache-Status: hit

如您所见,即使请求中的 Host 标头未指定端口,我们也已获得缓存的响应。这证明端口已从缓存键中排除。重要的是,完整的标头仍会传递到应用程序代码中,并反映在响应中。

总之,虽然Host标头已设置键值,但缓存对其进行转换的方式允许我们将有效载荷传递到应用程序中,同时仍保留一个“正常”的缓存键,该键将映射到其他用户的请求。这种行为是我们将在本节中讨论的所有漏洞利用的关键概念。

您可以使用类似的方法来调查缓存对输入的任何其他处理。您的输入是否以任何方式被规范化?您的输入是如何存储的?您是否注意到任何异常情况?我们稍后将使用具体示例来介绍如何回答这些问题。


6.2.3 识别可利用的小工具

到目前为止,您应该对目标网站的缓存行为有相对深入的了解,并且可能已经在缓存键的构造方式中发现了一些有趣的缺陷。最后一步是确定一个合适的小工具,您可以将其与此缓存键缺陷链接在一起。这是一项重要的技能,因为任何 Web 缓存中毒攻击的严重性在很大程度上取决于您能够利用的小工具。

这些小工具通常是典型的客户端漏洞,例如反射型 XSS 和开放重定向。通过将这些与 Web 缓存中毒相结合,您可以大幅升级这些攻击的严重性,将反射的漏洞变成存储的漏洞。无需诱使受害者访问特制的 URL,您的有效载荷将自动提供给访问普通、完全合法 URL 的任何人。

也许更有趣的是,这些技术使您能够利用许多未分类的漏洞,这些漏洞通常被视为“不可利用”且未修补这包括在资源文件中使用动态内容,以及需要浏览器永远不会发送的格式错误的请求的漏洞


6.3 利用缓存键缺陷

现在您已经熟悉了高级方法,让我们看一下一些典型的缓存键缺陷以及如何利用它们。我们将介绍:

  • 未键端口
  • 未键查询字符串
  • 未键查询参数
  • 缓存参数隐藏
  • 规范化缓存键
  • 缓存键注入
  • 内部缓存中毒

6.3.1 未被用作缓存键的端口

Host 标头通常是缓存键的一部分,因此,最初似乎不太可能注入任何类型的有效负载。但是,某些缓存系统会解析标头并从缓存键中排除端口。

在这种情况下,您可能会使用此标头进行 Web 缓存中毒。例如,考虑我们之前看到的情况,其中重定向 URL 是根据 Host 标头动态生成的。这可能使您能够通过简单地向请求添加任意端口来构建拒绝服务攻击。所有浏览到主页的用户都将被重定向到一个哑端口,从而有效地关闭主页,直到缓存过期。

如果网站允许您指定非数字端口,则此类攻击可以进一步升级。例如,您可以使用它来注入 XSS 有效负载。


6.3.2 未键查询字符串

Host 标头一样,请求行通常是键控的。但是,最常见的缓存键转换之一是排除整个查询字符串。


检测未键查询字符串
如果响应明确告诉您是否收到了缓存命中,则此转换相对容易发现 - 但如果没有呢?这样做的副作用是,动态页面看起来好像是完全静态的,因为很难知道您是在与缓存通信还是与服务器通信。

要识别动态页面,通常你会观察更改参数值对响应的影响。但是,如果查询字符串未被用作缓存键,大多数时候你仍然会获得缓存命中,因此无论你添加任何参数,响应都不会改变。显然,这也使得经典的缓存破坏查询参数变得多余。

幸运的是,有其他添加缓存破坏器的方法,例如将其添加到一个带键的头部中,且不会干扰应用程序的行为。一些典型的例子包括:

Accept-Encoding: gzip, deflate, cachebuster
Accept: */*, text/cachebuster
Cookie: cachebuster=1
Origin: https://cachebuster.vulnerable-website.com

如果您使用 Param Miner,还可以选择“添加静态/动态缓存破坏器”和“在标头中包含缓存破坏器”选项。然后,它将自动向您使用 Burp 的手动测试工具发送的任何请求中的常用键控标头添加缓存破坏器。

另一种方法是查看缓存和后端对请求路径的规范化方式之间是否存在差异。由于路径几乎肯定是键控的,因此有时您可以利用这一点发出具有不同键但仍命中相同端点的请求。例如,以下条目可能会分别被缓存,但在后端被视为等同于GET /

Apache: GET //
Nginx: GET /%2F
PHP: GET /index.php/xyz
.NET GET /(A(xyz)/

这种转换有时会掩盖原本明显可见的 XSS 漏洞。如果渗透测试人员或自动化扫描器在未察觉的情况下仅接收到缓存响应,那么页面上可能看起来似乎没有反射型 XSS。


利用未键查询字符串
从缓存键中排除查询字符串实际上会使这些反射的 XSS 漏洞更加严重。

通常,此类攻击将依赖于诱使受害者访问恶意构建的 URL。但是,通过未键控的查询字符串在缓存中中毒将导致有效负载提供给访问本来完全正常的 URL 的用户。这可能会影响更多的受害者,而攻击者无需进一步互动。


未键查询参数
到目前为止,我们已经看到,在某些网站上,整个查询字符串都被排除在缓存键之外。但有些网站只排除与后端应用程序无关的特定查询参数,例如用于分析或提供目标广告的参数utm_content 等 UTM 参数是在测试期间检查的良好候选参数。

已从缓存键中排除的参数不太可能对响应产生重大影响。很可能不会有任何有用的小工具接受来自这些参数的输入。也就是说,某些页面以易受攻击的方式处理整个 URL,从而有可能利用任意参数。


6.3.3 缓存参数隐藏

如果缓存从缓存键中排除了一个无害的参数,并且你无法根据完整的 URL 找到任何可利用的小工具,那么你认为你已经走到了死胡同是可以原谅的。然而,这实际上是事情变得有趣的地方。

如果您可以弄清楚缓存如何解析 URL 以识别和删除不需要的参数,您可能会发现一些有趣的特性。特别值得关注的是缓存和应用程序之间的任何解析差异。这可能允许您通过在排除的参数中 “隐藏” 任意参数来将任意参数潜入应用程序逻辑中。

例如,事实上的标准是,如果参数是查询字符串中的第一个参数,则参数前面会带有问号 (?),或者前面是符号 (&)。一些编写不佳的解析算法会将任何 ?视为新参数的开头,无论它是否是第一个参数。

假设从缓存键中排除参数的算法以这种方式运行,但服务器的算法只接受第一个 ?作为分隔符。请考虑以下请求:

GET /?example=123?excluded_param=bad-stuff-here

在这种情况下,缓存将标识两个参数并从缓存键中排除第二个参数。但是,服务器不接受第二个 ?作为分隔符,而只看到一个参数 example,其值是查询字符串的整个其余部分,包括我们的有效负载。如果将 example 的值传递到一个有用的 gadget 中,则我们已成功注入 payload 而不会影响缓存键。


利用参数解析特性
在相反的场景中,可能会出现类似的参数隐藏问题,即后端识别出缓存未识别的不同参数。例如,Ruby on Rails 框架将符号 (&) 和分号 (;) 解释为分隔符。当与不允许这样做的缓存结合使用时,您可能会利用另一个特性来覆盖应用程序逻辑中键控参数的值。

请考虑以下请求:

GET /?keyed_param=abc&excluded_param=123;keyed_param=bad-stuff-here

顾名思义,keyed_param 包含在缓存键中,但 excluded_param 不包含。许多缓存只会将其解释为两个参数,由 & 符号分隔:

  1. keyed_param=abc
  2. excluded_param=123;keyed_param=bad-stuff-here

解析算法删除 excluded_param 后,缓存键将仅包含 keyed_param=abc。但是,在后端,Ruby on Rails 会看到分号,并将查询字符串拆分为三个单独的参数:

  1. keyed_param=abc
  2. excluded_param=123
  3. keyed_param=bad-stuff-here

但现在有一个重复的keyed_param。这就是第二个奇特之处发挥作用的地方。如果有重复的参数,每个参数具有不同的值,Ruby on Rails 会优先考虑最后出现的参数。最终结果是缓存键包含一个无害的、预期的参数值,允许缓存的响应像往常一样提供给其他用户。然而,在后端,同一个参数具有完全不同的值,这就是我们注入的有效载荷。正是这个第二个值将被传递到小工具中,并反映在被污染的响应中。

如果这个漏洞能让你控制一个将被执行的函数,那么它可能会特别强大。例如,如果一个网站正在使用 JSONP 进行跨域请求,这通常会包含一个callback参数,以便在返回的数据上执行一个给定的函数:

GET /jsonp?callback=innocentFunction

在这种情况下,您可以使用这些技术来覆盖预期的回调函数并改为执行任意 JavaScript。


利用 fat GET 支持
在特定情况下,HTTP 方法可能未被键入。这可能允许您使用正文中包含恶意负载的 POST 请求来毒害缓存。然后,您的有效负载甚至会响应用户的 GET 请求。尽管这种情况非常罕见,但有时只需向 GET 请求添加请求体即可创建“胖”GET 请求,从而实现类似的效果:

GET /?param=innocent HTTP/1.1
…
param=bad-stuff-here

在这种情况下,缓存键将基于请求行,但参数的服务器端值将从请求体中获取。

仅当网站接受具有请求体的 GET 请求时,才有可能这样做,但存在可能的解决方法。有时可以通过重写 HTTP 方法来鼓励“fat GET”处理,例如:

GET /?param=innocent HTTP/1.1
Host: innocent-website.com
X-HTTP-Method-Override: POST
…
param=bad-stuff-here

只要 X-HTTP-Method-Override 标头未键,您就可以提交伪 POST 请求,同时保留从请求行派生的 GET 缓存键。


在资源导入中利用动态内容
导入的资源文件通常是静态的,但有些文件反映来自查询字符串的输入。这通常被认为是无害的,因为浏览器在直接查看时很少执行这些文件,并且攻击者无法控制用于加载页面子资源的 URL。但是,通过将其与 Web 缓存中毒相结合,您偶尔可以将内容注入资源文件。

例如,考虑在 import 语句中反映当前查询字符串的页面:

GET /style.css?excluded_param=123);@import… HTTP/1.1

HTTP/1.1 200 OK
…
@import url(/site/home/index.part1.8a6715a2.css?excluded_param=123);@import…

您可以利用此行为注入恶意 CSS,从而从导入 /style.css 的任何页面中泄露敏感信息。

如果导入 CSS 文件的页面没有指定doctype,您甚至可以利用静态 CSS 文件。如果配置正确,浏览器将简单地搜索文档以查找 CSS,然后执行它。这意味着你可以偶尔通过触发反映被排除的查询参数的服务器错误来污染静态 CSS 文件:

GET /style.css?excluded_param=alert(1)%0A{}*{color:red;} HTTP/1.1

HTTP/1.1 200 OK
Content-Type: text/html
…
This request was blocked due to…alert(1){}*{color:red;}

6.3.4 规范化缓存键

应用于缓存键的任何规范化也可能引入可利用的行为。事实上,它偶尔可以启用一些原本几乎不可能的漏洞。

例如,当您在参数中找到反射的 XSS 时,它在实践中通常是不可利用的。这是因为现代浏览器通常会在发送请求时对必要的字符进行 URL 编码,而服务器不会对它们进行解码。预期受害者收到的响应将仅包含无害的 URL 编码字符串

某些缓存实现会在将键控输入添加到缓存键时对其进行规范化。在这种情况下,以下两个请求将具有相同的键:

GET /example?param="><test>
GET /example?param=%22%3e%3ctest%3e

此行为可以允许您利用这些原本“不可利用”的 XSS 漏洞。如果您使用 Burp Repeater 发送恶意请求,则可以使用未编码的 XSS 负载在缓存中植入病毒。当受害者访问恶意 URL 时,他们的浏览器仍将对有效负载进行 URL 编码;但是,一旦 URL 被缓存规范化,它将具有与包含未编码有效负载的响应相同的缓存键。

因此,缓存将提供中毒的响应,有效负载将在客户端执行。您只需要确保在受害者访问 URL 时缓存中毒。


6.3.5 缓存键注入

您有时会在键控标头中发现客户端漏洞。这也是一个典型的“不可利用”问题,有时可以通过缓存中毒来利用。

键控组件通常捆绑在一个字符串中以创建缓存键。如果缓存没有在组件之间实现分隔符的正确转义,则可能会利用此行为来构建具有相同缓存键的两个不同请求。

以下示例使用双下划线分隔缓存键中的不同组件,并且不对其进行转义。您可以利用这一点,方法是首先使用在相应的键控标头中包含有效负载的请求来毒害缓存:

GET /path?param=123 HTTP/1.1
Origin: '-alert(1)-'__

HTTP/1.1 200 OK
X-Cache-Key: /path?param=123__Origin='-alert(1)-'__

<script>'-alert(1)-'</script>

如果您随后诱使受害者用户访问以下 URL,他们将收到中毒响应:

GET /path?param=123__Origin='-alert(1)-'__ HTTP/1.1

HTTP/1.1 200 OK
X-Cache-Key: /path?param=123__Origin='-alert(1)-'__
X-Cache: hit

<script>'-alert(1)-'</script>

6.3.6 内部缓存投毒

到目前为止,我们已经研究了如何利用外部 Web 缓存的实施方式中的缺陷来暴露隐藏在看似键控的组件中的扩展攻击面。但是,除了使用独特的外部组件外,某些网站还会直接在应用程序中实现缓存行为。这有几个优点,例如避免我们之前看到的那种解析差异。

由于这些集成缓存是专门为特定应用程序构建的,因此这也使开发人员可以自由地在更大程度上定制其行为。因此,这些缓存有时可能会以不寻常的方式运行,而您通常不会从需要与多个应用程序兼容的更标准化的外部缓存中看到这些行为。有时,这些奇怪的行为也可能为一些高严重性的缓存中毒漏洞提供机会。

其中一些缓存不是缓存整个响应,而是将响应分解为可重用的片段,并单独缓存每个片段。例如,用于导入广泛使用的资源的代码段可能存储为独立缓存条目。然后,用户可能会收到一个响应,其中包含来自服务器的内容混合,以及来自缓存的多个单独片段。

由于这些缓存片段旨在跨多个不同的响应重复使用,因此缓存键的概念实际上并不适用。每个包含给定片段的响应都将重用相同的缓存片段,即使响应的其余部分完全不同。在这种情况下,缓存投毒可能会产生广泛的影响,特别是如果您对每个页面都使用的片段进行投毒。由于没有缓存键,您只需一个请求就可以对每个用户的每个页面进行投毒。

这通常只需要您使用基本的 Web 缓存中毒技术,例如操作 Host 标头。


如何识别内部缓存
集成的应用程序级缓存带来的挑战之一是,由于通常没有面向用户的反馈,因此很难识别和调查它们。要识别这些缓存,您可以寻找一些迹象。

例如,如果响应反映了您发送的最后一个请求的输入和上一个请求的输入的混合,则这是一个关键指标,表明缓存存储的是片段而不是整个响应。如果您的输入反映在多个不同页面的响应中,尤其是在您从未尝试注入输入的页面上,这同样适用。

其他时候,缓存的行为可能非常不寻常,以至于最合乎逻辑的结论是它必须是唯一且专用的内部缓存。

当网站实施多层缓存时,可能会难以理解幕后发生的事情并了解网站的缓存系统的行为方式。


安全地测试内部缓存
在测试普通的网页缓存时,我们建议使用缓存破坏器,以防止你的恶意响应被提供给其他用户。然而,如果集成缓存没有缓存键的概念,那么传统的缓存破坏器就无用了。这意味着很容易意外地使真正用户的缓存中毒。

因此,在测试这些类型的漏洞时,请务必尽最大努力减轻潜在损害。在发送每个请求之前,请仔细考虑注入的有效负载的影响。特别是,您应该确保仅使用您控制的域来毒害缓存,而不是一些任意的 “evil-user.net”。这样,如果出现问题,您可以控制接下来会发生什么。


7. 如何防止 Web 缓存中毒漏洞

防止 Web 缓存中毒的最终方法显然是完全禁用缓存。虽然对于许多网站来说,这可能不是一个现实的选择,但在其他情况下,这可能是可行的。例如,如果你使用缓存仅仅是因为在采用内容分发网络(CDN)时它是默认开启的,那么评估默认的缓存选项是否确实反映了你的需求可能是值得的。

即使你确实需要使用缓存,将其限制为纯静态响应也是有效的,前提是你对自己归类为“静态”的内容要足够谨慎。例如,确保攻击者无法欺骗后端服务器获取他们恶意版本的静态资源而不是真正的资源。

这也与有关 Web 安全性的更广泛观点有关。现在,大多数网站都将各种第三方技术整合到其开发流程和日常运营中。无论您自己的内部安全态势多么强大,只要您将第三方技术整合到您的环境中,您就依赖于其开发人员也和您一样具有安全意识。基于您最薄弱环节的安全性,确保在集成任何第三方技术之前完全了解其安全影响至关重要。

特别是在 Web 缓存中毒的情况下,这不仅意味着决定是否默认打开缓存,还意味着查看您的 CDN 支持哪些标头。上面讨论的几个 Web 缓存中毒漏洞之所以被暴露出来,是因为攻击者能够纵一系列晦涩难懂的请求标头,其中许多请求标头对于网站的功能来说是完全不必要的。同样,您可能在没有意识到的情况下将自己暴露在此类攻击中,这纯粹是因为您已经实施了一些默认情况下支持这些未键输入的技术。如果网站不需要标头即可工作,则应将其禁用。

在实施缓存时,您还应采取以下预防措施:

  • 如果您出于性能原因考虑从缓存键中排除某些内容,请改为重写请求。
  • 不接受比较复杂的 GET 请求。请注意,默认情况下,某些第三方技术可能允许这样做。
  • 修补客户端漏洞,即使它们看起来无法利用。由于缓存行为中存在不可预测的玄学问题,其中一些漏洞实际上可能被利用。有人发现使此漏洞可利用的技巧(无论是基于缓存的还是其他的)可能只是时间问题。

http://www.kler.cn/a/580038.html

相关文章:

  • 关于ModbusTCP/RTU协议转Ethernet/IP(CIP)协议的方案
  • 解锁 AI 量化新境界:Qbot 携手 iTick
  • 探索Java中的多态
  • HCIP第二讲作业
  • 目标检测YOLO实战应用案例100讲-基于毫米波雷达的多目标检测 (续)
  • ART-PI2 上手记录(一)
  • 【每日学点HarmonyOS Next知识】状态栏控制、片段按钮点击回调、绘制组件、取消按钮与输入框对齐、父调子组件方法
  • FPGA|Verilog-SPI驱动
  • 群晖DS223 Docker搭建为知笔记
  • LLM中的transformer结构学习(二 完结 Multi-Head Attention、Encoder、Decoder)
  • STM32(G4)高级定时器的应用(计数模式)的原理
  • k8s启动时calico-kube-controllers与coredns组件一直是pending状态
  • 轻松解密 PDF 密码的实用方法
  • DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加列宽调整功能,示例Table14基础固定表头示例
  • Qt 元对象系统
  • Talking Head Review (数字人算法综述)
  • RabbitMq--消息可靠性
  • 【由技及道】量子跃迁部署术:docker+jenkins+Harbor+SSH的十一维交付矩阵【人工智障AI2077的开发日志011】
  • word甲烷一键下标
  • 学习笔记11——并发编程之并发关键字