Next.js 和缓存中毒:黑洞探索

admin 2024年7月10日23:01:13评论3 views字数 7105阅读23分41秒阅读模式

Next.js 和缓存中毒:黑洞探索

介绍

为了寻找挑战、零日漏洞和赏金,我专注于广泛使用的软件,以寻找有趣的缓存中毒案例。我的注意力很快转向了Next.js,这是一个基于 React 的开源 JavaScript 框架,由 Vercel 开发和维护。Next 软件包每周的下载量超过 600 万次,表明其使用范围广泛。该软件中的任何发现都可能影响许多用户,这就是我撸起袖子所需要的全部动力。

本文将重点介绍缓存中毒。如果您不熟悉此漏洞,我强烈建议您先阅读我的文章“通过 Mozilla 上的缓存中毒进行 DOS 攻击”。

指数

  • 第一个漏洞:开始研究、幻灭和 n-day

  • 第二个漏洞:React 服务器组件和 CDN,食物中毒

  • 第三个漏洞:内部标头、HTTP 状态和错误页面

  • 结论和奖励

第一个漏洞:开始研究、幻灭和 n-day

我很快发现了与 Next.js 中间件相关的一个有趣行为。在预取 SSR(服务器端渲染)页面的数据时,添加x-middleware-prefetch标头会导致响应为空的 JSON 对象{}。如果存在 CDN 或缓存系统,则此空响应可能会被缓存 — 具体取决于缓存规则配置 — 导致目标页面不切实际且其内容无法访问。

Next.js 和缓存中毒:黑洞探索

可重复性/可利用性的主要条件是存在缓存系统。如果没有它,响应将不会被缓存,并且添加标头所导致的行为将是无害的。

我不会在这里深入研究getServerSidePropsgetStaticProps函数的技术细节,但它们分别用于检索服务器端数据和生成静态页面。从版本 13.4.20-canary.13开始,Next.js 为 SSR 响应添加了cache-control以防止它们被缓存。

Next.js 和缓存中毒:黑洞探索

想到发现了一个新的零日漏洞,我兴奋不已,但很快意识到该漏洞已分配了 CVE(CVE-2023-46298)。但是,由于概念验证 (POC) 尚未共享,我仍然能够大规模利用此漏洞。数十个漏洞赏金计划受到影响,产生了大量赏金,这极大地激励了我,并鼓励我继续研究。

Next.js 和缓存中毒:黑洞探索

弹性信息:非详尽列表

影响和严重性

这是一种通过缓存中毒发起的 DOS 攻击,CVSS 评分为 7.5:CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N /I:N/A:H/CR:X/IR:X/AR:X

第二个漏洞:React 服务器组件和 CDN,食物中毒

首先,什么是 React Component Server (RSC)?来自next.js 官方文档的答案:

RSC Payload 是渲染的 React Server Components 树的紧凑二进制表示。React 在客户端使用它来更新浏览器的 DOM。RSC Payload 包含:

  • 服务器组件的渲染结果

  • 客户端组件应呈现的位置的占位符以及对其 JavaScript 文件的引用

  • 从服务器组件传递到客户端组件的任何 props

简单来说,这个功能可以让 React 组件在发送到客户端之前直接在服务器上渲染。这可以减少客户端的负载,从而提高性能,并允许直接访问服务器端资源。

在研究 Next.js 应用程序的过程中,我经常遇到检索 RSC 有效负载的请求。最初,这并没有引起我的好奇心。负责此行为的标头 — Rsc: 1— 通过响应标头添加到缓存键中Vary,理论上可以防止缓存中毒。

Next.js 和缓存中毒:黑洞探索

通过检查我的代理上的 HTTP 历史记录,我注意到获取 RSC 有效负载的请求包含系统添加的URL 参数_rsc=randomValue:。

我很快就明白了,这是一个用于防止缓存“不需要的”响应的缓存破坏器,这令人惊讶,因为如前所述,标头是通过添加到缓存键的vary。经过一番研究,我偶然发现了几个月前的一张 GitHub 票,其中一名用户抱怨看到 RSC 有效负载被缓存,这阻止了对内容的访问。框架维护者之一 Tim Neutkens回应(于 2023 年 4 月 20 日):

为了澄清起见,App Router 在客户端导航时向获取添加了一些标头,以获取 RSC 有效负载而不是 HTML。服务器上的 Next.js 设置 Vary 标头,以确保浏览器/CDN 可以根据传递给获取的标头进行缓存:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary

由于提供了 Vary 标头,因此将关闭此问题,所以这是一个 CDN 配置问题。

因此很明显,该框架本身正在缓存中毒,而添加 URL 参数是为了防止这种行为。在检查了提交历史记录后,我注意到在 2023 年 6 月 12 日(即 timneutkens 对 github 票证做出回应的两个月后)推送了一个“修复”的类似内容。

Next.js 和缓存中毒:黑洞探索

Next.js 和缓存中毒:黑洞探索

如果Rsc添加了标头,为什么 CDN 仍会缓存响应而不在内容协商vary阶段考虑它?

vary来自Mozilla 文档的关于响应标头功能的快速提醒:

Vary HTTP 响应标头描述了请求消息中除方法和 URL 之外的部分,这些部分影响了其所发生响应的内容。通常,它用于在使用内容协商时创建缓存键。

因此,next.js 的维护者认为,鉴于框架系统地在标头中添加了标头,这不是他们的责任vary。因此,不符合此 HTTP 标准不是他们的责任,而是不遵守这些要求的 CDN 的责任。

CDN 真的不尊重vary价值观吗?

尽管这可能令人惊讶,但在深入研究了各种 CDN 的文档后,事实证明在某些情况下确实如此。以下是我在一些流行的 CDN 上发现的:

  • 在Cloudflare中,它是分类的(来自官方文档):

vary — Cloudflare 在缓存决策中不考虑变化值。不过,当配置了图像的 Vary 并且变化标头为 evolve: accept-encoding 时,会考虑变化值。
  • Cloudfront删除或替换了一些标头,包括vary:
如果您将源配置为在 Vary 标头中包含任何其他值,则 CloudFront 会在将响应返回给查看器之前删除这些值。(来自官方文档)
  • Akamai有一种称为Remove Vary Header 的行为,顾名思义,它允许删除标头。Akamai 边缘服务器不会缓存包含标头的响应,即使内容根据定义是可缓存的,但也有“例外”(来自官方文档):varyVary

Next.js 和缓存中毒:黑洞探索

因此,“自适应媒体交付”、“下载交付”和“对象交付”产品的默认行为是删除vary响应标头。

那么,这可以被利用吗?

Next.js 和缓存中毒:黑洞探索

我们之前讨论了由框架引起的自缓存中毒问题,促使 Next.js 开发人员实施“修复”:在获取操作期间添加 URL 参数以充当缓存破坏器并防止意外缓存 RSC 有效负载。但是,此措施并不能阻止攻击者通过发送带有标头的请求Rsc(不使用缓存破坏器)来利用该漏洞,这模仿了典型的缓存中毒攻击。这种攻击是否成功自然取决于CDN 及其缓存规则。

从理论上来说,它是可以被利用的,我必须能够通过真实的目标来确认这一切,经过一番研究之后,我得到了第一个结果:

Next.js 和缓存中毒:黑洞探索

缓存被毒害,主页/root页面返回的是 React 组件服务器而不是初始内容,导致赏金达到 2000 美元。从那时起,和往常一样,我进行了模式提取、模板创建和大规模扫描包含漏洞赏金资产的数据库,然后向存在漏洞的程序发送报告,以下是其中一些:

Next.js 和缓存中毒:黑洞探索

弹性信息:非详尽列表

影响和严重性

这是一种通过缓存中毒发起的 DOS 攻击,CVSS 评分为 7.5:CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N /I:N/A:H/CR:X/IR:X/AR:X

向安全团队上报漏洞

我联系了 Next.js 团队并与他们讨论了这个问题。他们得出的结论是,在框架层面他们无能为力。虽然我知道这不完全是他们的错/责任,但我认为,如果不通知用户,让他们实施临时解决方案来降低风险,就让问题悬而未决,这是一个令人担忧的问题。

时间线:

  • 2024 年 3 月 23 日报告的漏洞

  • Vercel 于 2024 年 4 月 3 日首次回复

  • Vercel 要求在 2024 年 4 月 17 日提供更多信息

  • Vercel 最终回应 2024 年 4 月 29 日

第三个漏洞:内部标头、HTTP 状态和错误页面

短暂停止研究后,我决定重新开始。我在每个屏幕上都显示了 Next.js 源代码,手里拿着一杯咖啡,带着我值得信赖的调试伙伴,老好人console.log(是吗?)。BismiLlah,我已准备好进行一些密集的代码审查。

在我的评论进一步深入之后,我偶然发现了一段立即引起我好奇心的代码:

Next.js 和缓存中毒:黑洞探索

因此,在某些条件下(我们稍后会看到),可以通过请求中直接提供的标头值覆盖状态代码( )并调用/返回错误页面(源)。非常有趣,我继续挖掘,发现这是一个“内部”标头,因此当它们来自客户端时通常会被剥离:x-invoke-status req.header[] x-invoke-status

Next.js 和缓存中毒:黑洞探索

显然,需要进行一个小的本地测试来验证是否确实如此,或者至少探索某些条件下的可能性。最初,我尝试使用默认配置并将其x-invoke-status: 888作为标头包含在内,第一次尝试时:

Next.js 和缓存中毒:黑洞探索

Next.js 和缓存中毒:黑洞探索

到目前为止,我对该载体可用于缓存中毒的可利用性毫不怀疑。通过指定 HTTP 状态代码200,它允许遵守大多数缓存规则(通常最初不允许缓存错误代码)。这可以缓存错误页面的内容,然后可以将其作为响应而不是主页提供。

注意:上面讨论的另外两个漏洞由于同样的原因而特别有趣:添加各自的标头所引起的行为会导致响应200,这与大多数缓存规则一致。

代码如何工作

如代码所示,在特定条件下,header 的值会覆盖response 对象的属性x-invoke-status值。然后返回页面(无论指定的 HTTP 代码是什么):statusCode/_error

res.statusCode = Number(req.headers['x-invoke-status'])

(...)

return this.renderError(err, req, res, '/_error', parsedUrl.query)

因此,可以指定任何 HTTP 代码来更改响应代码并返回错误页面( /_error)。通常,CDN 和缓存系统配置为不缓存 HTTP 错误代码。但是,攻击者可以指定代码200以符合缓存规则并有效地“强制”缓存错误页面,如前所述。

要实现这种行为,必须满足三个条件:

const useInvokePath =         !useMatchedPathHeader &&         process.env.NEXT_RUNTIME !== 'edge' &&         invokePath

1、useMatchedPathHeader必须返回false,如果出现以下情况则属于这种情况:

const useMatchedPathHeader =         this.minimalMode && typeof req.headers['x-matched-path'] === 'string'
最小模式是一种实验性配置,正在功能齐全的 Next.js 环境(如 Vercel)中进行测试,这些环境具有边缘网络来处理下一个服务器通常处理的请求的特定部分。(来源)
  1. Next.js有两个服务器运行时,使用的 nextjs 运行时一定不是edge。默认使用的运行时是Node.js
  2. invokePath应该返回true

const invokePath = req.headers['x-invoke-path'] as string

经过本地测试,我发现在请求中提供此标头时不会考虑此标头,并且似乎不会检索其值。但是,标头的值会自动添加并对应于当前路径。因此,常量将始终返回true

如果x-invoke-error提供了标题并且错误页面模板需要它,那么也可以显示自定义错误消息(必须以 json 格式提供该值 {"message":"<>"}):

if (typeof req.headers['x-invoke-error'] === 'string') {        const invokeError = JSON.parse(            req.headers['x-invoke-error'] || '{}'        )        err = new Error(invokeError.message)    }

Next.js 和缓存中毒:黑洞探索

现实世界的利用

在本地确认了漏洞后,我迅速采取行动,在真实目标(漏洞赏金计划)上验证该漏洞。我再次迅速实现了第一次成功利用,之后又进行了多次利用,其中一次获得了3000 美元的赏金。

Next.js 和缓存中毒:黑洞探索

Next.js 和缓存中毒:黑洞探索

影响和严重性

这是一种通过缓存中毒发起的 DOS 攻击,CVSS 评分为 7.5:CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N /I:N/A:H/CR:X/IR:X/AR:X

向安全团队上报漏洞

在向 Next.js 安全团队报告此漏洞后,他们最初承认这是一个与实验性最小模式功能相关的有效错误。他们提到他们有潜在的修复选项。

这段时间,由于时间很忙,我没有深入研究这个问题是否确实来自最小化模式,相信他们正在解决这个问题。然而,与我观察到的早期情况(第 1 种)相比,我发现这很奇怪。

接下来是回应的时间,我不会谈论某些机密细节,但总结一下,他们认为最终这个漏洞是 CDN 缓存的结果,而不是 Next.js 中存在的漏洞,并且可利用条件是有限的,基于 Next.js 之外的因素(WAF/CDN 配置)。

令我惊讶的是,他们的回复早在几个小时前就提交了一个漂亮的提交,针对我的发现进行了修复:

Next.js 和缓存中毒:黑洞探索

Next.js 和缓存中毒:黑洞探索

这为什么没有意义呢?

无论问题的根源是否来自,都minimalMode无法改变这样一个事实:客户端通过内部x-invoke-status标头提供的值已被考虑在内,并且对覆盖 HTTP 代码和调用错误页面的响应产生了影响,这就是他们实施修复的原因

使用内部标头x-invoke-status可以通过最低配置(默认)覆盖状态代码并调用错误页面。现在关于缓存,从我们能够使用这个标头的那一刻起 -通常是内部的,如代码中指定的(见上文) - 无需框架添加它(例如 RSC 标头的情况)来避免潜在的缓存中毒,无论开发团队使用(或不使用)vary什么缓存系统,他们的责任都是直接承担的。

如前所述,通过指定状态来调用错误页面200将满足大多数缓存规则(其中大多数不允许缓存带有错误状态代码的响应),并且如果实现了缓存系统,则将允许缓存此错误页面。

  1. 可能使用内部标头对响应产生影响

  2. 事实上,没有建立任何系统来避免使用标头和/或缓存由此产生的响应

以上两点直接涉及框架,用户有必要意识到此漏洞并能够保护自己免受其侵害。我认为 CVE 比 更重要的原因在于x-middleware-prefetch——尽管不同——同样意味着框架的责任(也需要缓存系统来利用它……)。

时间线:

  • 2024 年 5 月 22 日报告漏洞

  • Vercel 于 2024 年 6 月 5 日首次回复

  • Vercel 于 2024 年 6 月 13 日验证了该漏洞

  • CVE 提案 - 由我于 2024 年 6 月 13 日提出

  • Vercel 于 2024 年 6 月 18 日提交修复

  • Vercel 于 2024 年 6 月 18 日改变了对其责任的看法

  • Vercel 最终答复,案件于 2024 年 6 月 21 日结案

结论

您可能已经明白,Vercel 安全团队认为问题已解决并且“不认为这是 next.js 中直接漏洞的结果”(尽管已提交)我正在分享此漏洞以告知社区。根据我有限的经验,每次我向公司报告漏洞/零日漏洞时,我总是 -无一例外- 收到相当令人失望的回复。我显然不想把我的案例泛泛而谈,但这可能会阻止多名研究人员花时间负责任地报告漏洞,这是相当令人遗憾的。我可以理解一些公司将 CVE 视为“耻辱的徽章”,但当软件被广泛使用时,他们有责任承担并展示模范的透明度。

就我而言,我能够发送几十份与本文中引用的漏洞相关的报告(仅限 BBP),并且我能够找到200 多个易受攻击的资产(也许更多,我不确定)。

感谢我的朋友Jayesh Madnani,我能够系统地扫描他庞大的数据库(我的数据库还不到它的一半大小)上的发现。与他合作总是一件愉快的事,得益于他的自动化专业知识,可以定位尽可能多的网站。

奖励:对某些指控的回应,旨在降低特定缓存中毒报告的严重性

我在这方面经验丰富,我经常将 CP 报告的严重性从Medium改为High甚至从Informative改为Triaged,有时只需几个参数就可以将严重性从 0 美元改为 3000 美元。考虑到缓存中毒的概念并不总是被很好地理解,尽可能清晰明确,假设你的对话者对这个主题一无所知。

声明 1:“影响不大,缓存仅持续 XXXX 秒”

回应:缓存的持续时间无关紧要,攻击者只需编写一个小脚本,每 X 秒发送一次请求(=缓存持续时间),这样缓存就会永久中毒,使网站完全不可用。(请随意将脚本附加到 POC)

声明 2:“影响不大,缓存投毒只在预定义区域有效,当我访问包含缓存破坏器的链接时,响应正常”

回应:您可能知道,出于性能原因,缓存/CDN 系统通常配置为按区域运行。尽管如此,在实际情况下,攻击者只需从来自不同区域的 IP 发送请求(通过 VPN)即可影响所有用户。

声明 3:“我们使用 CVSS 计算,评级为 5.3 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L”

响应:CVSS 分数设置不正确,漏洞导致页面在无限期内完全不可用/不可行,可用性参数设置为高,因此分数为 7.5(高):CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/CR:X/IR:X/AR:X

高 (H):可用性完全丧失,导致攻击者能够完全拒绝对受影响组件中的资源的访问;这种丧失要么是持续的(攻击者继续发动攻击时),要么是持续的(攻击完成后这种情况仍然存在)。或者,攻击者有能力拒绝部分可用性,但可用性丧失会给受影响的组件带来直接、严重的后果 (…)
https://zhero-web-sec.github.io/research-and-things/nextjs-and-cache-poisoning-a-quest-for-the-black-hole

原文始发于微信公众号(Ots安全):Next.js 和缓存中毒:黑洞探索

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月10日23:01:13
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Next.js 和缓存中毒:黑洞探索http://cn-sec.com/archives/2935859.html

发表评论

匿名网友 填写信息