CVE-2023-46729:Sentry Next.js SDK中的URL重写漏洞分析
2023-11-18 13:59:20 Author: Ots安全(查看原文) 阅读量:5 收藏

2023 年 11 月 9 日,Sentry 在其博客上发布了一篇题为Next.js SDK 安全建议 - CVE-2023-46729 的文章。文章讨论了CVE-2023-46729漏洞的详细信息,包括其原因、发现时间和修补时间。

虽然该漏洞于11/9正式公布,但实际上已在10/31发布的7.77.0版本中修复。给开发人员一些时间来修补该漏洞。

下面我们简单讨论一下这个漏洞的成因和攻击方法。

漏洞分析

GitHub 上还有更多技术说明:CVE-2023-46729:SSRF via Next.js SDK 隧道端点:

https://github.com/getsentry/sentry-javascript/security/advisories/GHSA-2rmr-xw8m-22q9

你可以看这一段:

Next.js SDK 隧道端点的未经净化的输入允许将 HTTP 请求发送到任意 URL 并将响应反射回用户。

在Sentry中,有一个称为“隧道”的功能,官方文档中的这张图片完美地解释了为什么需要隧道:


如果没有隧道,发送到 Sentry 的请求将直接通过前端的浏览器发送。但是,这些直接发送到 Sentry 的请求可能会被广告拦截器拦截,从而导致 Sentry 无法接收数据。如果启用了隧道,请求首先发送到用户自己的服务器,然后转发到Sentry。这样,该请求就成为同源请求,不会被广告拦截器拦截。

在专门为 Next.js 设计的 Sentry SDK 中,使用了一个称为重写的功能。这是官方文档中的一个示例:

module.exports = {  async rewrites() {    return [      {        source: '/blog',        destination: 'https://example.com/blog',      },      {        source: '/blog/:slug',        destination: 'https://example.com/blog/:slug', // Matched parameters can be used in the destination      },    ]  },}

Next.js重写可以分为两种:内部重写和外部重写。后者更像是一个代理,因为它可以直接将请求重定向到外部网站并显示响应。

Next.js Sentry SDK 的实现位于sentry-javascript/packages/nextjs/src/config/withSentryConfig.ts中:

/** * Injects rewrite rules into the Next.js config provided by the user to tunnel * requests from the `tunnelPath` to Sentry. * * See https://nextjs.org/docs/api-reference/next.config.js/rewrites. */function setUpTunnelRewriteRules(userNextConfig: NextConfigObject, tunnelPath: string): void {  const originalRewrites = userNextConfig.rewrites;   // This function doesn't take any arguments at the time of writing but we future-proof  // here in case Next.js ever decides to pass some  userNextConfig.rewrites = async (...args: unknown[]) => {    const injectedRewrite = {      // Matched rewrite routes will look like the following: `[tunnelPath]?o=[orgid]&p=[projectid]`      // Nextjs will automatically convert `source` into a regex for us      source: `${tunnelPath}(/?)`,      has: [        {          type: 'query',          key: 'o', // short for orgId - we keep it short so matching is harder for ad-blockers          value: '(?<orgid>.*)',        },        {          type: 'query',          key: 'p', // short for projectId - we keep it short so matching is harder for ad-blockers          value: '(?<projectid>.*)',        },      ],      destination: 'https://o:orgid.ingest.sentry.io/api/:projectid/envelope/?hsts=0',    };     if (typeof originalRewrites !== 'function') {      return [injectedRewrite];    }     // @ts-expect-error Expected 0 arguments but got 1 - this is from the future-proofing mentioned above, so we don't care about it    const originalRewritesResult = await originalRewrites(...args);     if (Array.isArray(originalRewritesResult)) {      return [injectedRewrite, ...originalRewritesResult];    } else {      return {        ...originalRewritesResult,        beforeFiles: [injectedRewrite, ...(originalRewritesResult.beforeFiles || [])],      };    }  };}

关键部分是这一部分:

const injectedRewrite = {  // Matched rewrite routes will look like the following: `[tunnelPath]?o=[orgid]&p=[projectid]`  // Nextjs will automatically convert `source` into a regex for us  source: `${tunnelPath}(/?)`,  has: [    {      type: 'query',      key: 'o', // short for orgId - we keep it short so matching is harder for ad-blockers      value: '(?<orgid>.*)',    },    {      type: 'query',      key: 'p', // short for projectId - we keep it short so matching is harder for ad-blockers      value: '(?<projectid>.*)',    },  ],  destination: 'https://o:orgid.ingest.sentry.io/api/:projectid/envelope/?hsts=0',};

它根据op查询字符串参数确定要重定向到的最终 URL。

这里的问题是这两个参数都使用.*正则表达式,它匹配任何字符。换句话说,对于以下 URL:

https://huli.tw/tunnel?o=abc&p=def

它将代理:

https://oabc.ingest.sentry.io/api/def/envelope/?hsts=0

看起来不错,但是如果是这样呢?

https://huli.tw/tunnel?o=example.com%23&p=def

%23是 的 URL 编码结果#。它将被代理至:

https://oexample.com#.ingest.sentry.io/api/def/envelope/?hsts=0

我们使用#将原始主机名作为哈希的一部分包含在内,并成功更改代理的目的地。然而,领先o有点烦人。@让我们通过在开头添加来摆脱它:

https://huli.tw/[email protected]%23&p=def

它成为了:

https://[email protected]#.ingest.sentry.io/api/def/envelope/?hsts=0

这样,攻击者就可以使用该o参数来更改代理的目的地,并将请求重定向到任何地方。如前所述,此重写功能直接返回响应。因此,当用户访问时https://huli.tw/tunnel?o=@example.com%23&p=def,他们会看到 的响应example.com

换句话说,如果攻击者将请求重定向到自己的网站,他们就可以输出<script>alert(document.cookie)</script>,从而将其变成 XSS 漏洞。

如果攻击者将请求重定向到类似 的其他内部网页https://localhost:3001,则成为 SSRF 漏洞(但目标必须支持 HTTPS)。

至于修复,很简单。只需向正则表达式添加一些限制即可。最后,Sentry 将其调整为仅允许数字:

{  type: 'query',  key: 'o', // short for orgId - we keep it short so matching is harder for ad-blockers  value: '(?<orgid>\\d*)',},

此问题已在 7.77.0 及更高版本中修复。

结论

该漏洞非常简单且易于重现。只需找到修复提交并查看代码即可了解如何利用它。

总而言之,在进行 URL 重写时,您确实需要谨慎,因为很容易遇到问题(尤其是当您不仅重写路径而是整个 URL 时)。

感谢您抽出

.

.

来阅读本文

点它,分享点赞在看都在这里


文章来源: http://mp.weixin.qq.com/s?__biz=MzAxMjYyMzkwOA==&mid=2247502806&idx=1&sn=94e8ec0b6d454b3d0d3ee28a0cd3cb23&chksm=9bad829dacda0b8b98018e04c5bd43db283057a2c6f641c9dcc644d0f59b7e5042b20019c910&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh