Exploiting Web3’s Hidden Attack Surface: Universal XSS on Netlify’s Next.js Library
Three vulns that were discovered in Netlify’s Next.js lib, which is heavily used across many cryptocurrency sites due to it’s web3 support. With that context in mind, CIA (confidentiality, integrity, availability) is interesting with web3, as integrity is critical; the data coming from a trusted site needs to be trustworthy, as most users won’t go digging through the blockchain to verify a particular address or transaction matches.
1. Open Redirect on “_next/image” via Improper Path Parsing
The _next/image
handler is used for loading local resources, and takes a url
parameter from the user, which is used to send a mock HTTP request. The problem is you can pass an unencoded backslash in the url
parameter, which will get processed by Next.js. This is problematic because Next.js servers have a default behavior where users are redirected if they try to access a directory that’s inaccessible (such as \
). By passing ?url=/\/\example.com/...
, they were able to trigger this behavior and get the user redirected to an arbitrary host.
2. XSS and SSRF on “netlify/ipx” via Improper Host Parsing due to Reliance on Vulnerable “unjs/ufo” Library
Similar sort of issue to the first one, where this /_ipx/w_200/X
would take X
to load a resource. Netlify’s ipx module would allow external resource loading, but only on whitelisted hosts. Bug here is, seems the parsing that unjs/ufo
was doing (which Netlify relies on) was bugged in a similar way to the first issue. By passing a whitelisted host, followed by an encoded backslash, followed by a malicious host, Netlify would follow the second attacker-provided URL despite the fact it’s not a whitelisted domain. XSS can be achieved by providing a malicious svg.
3. Universal XSS and SSRF on “netlify-ipx” via Improper Handling of “x-forwarded-proto” Header and Abusable Cache Mechanism
The final issue was also in the /_ipx/w_200
route, and they discovered the IPX code could read the x-forwarded-proto
header to allow other protocols to be used. If it was specified, they’d simply take that value as-is and use it to build the request URL, like so:
${protocol}://${host}${id.startsWith('/') ? '' : '/'}${id}
By simply providing an x-forwarded-proto
header with a malicious URL, the whitelisting can similarly be bypassed to get a resource loaded from an attacker-controlled URL, giving the same impact as the second issue, but without the drawback of needing a whitelisted domain in the payload.