Full Report
Many website uses Static Site Generators alongside an Image CDN to optimize the images on the website being loaded, such as NextJs, which this website uses. The image CDN behind the scenes has a URL parameter for the image. The allowed URLs are typically inside an allowlist with some Content-Type validation as well. On Netlify, the endpoint is /.netlify/images?url=. The author placed the main paeg into this endpoint, with the requested content-type being text/html, and got a response of HTML! So, if we could find an arbitrary file upload on the site, we could achieve XSS through this endpoint. For the website, all of the images are uploaded on the same CDN website. Using this, it's trivial to upload a file with arbitrary content but it must have a valid content-type. When going to the CDN, it pops an alert box. However, when trying the same through the image endpoint it doesn't work because of the CSP. How does this CSP work? It turns out that it's a dynamic nginx configuration! If the location was on the /.netlify/images?url= path, then it returned script-src: 'none'. If we could trick nginx to see a different URL but have it parse the images endpoint then we would have a CSP bypass. The author tried /./.netlify/images?url=... which nginx will parse differently than the underlying application. Neat! The CSP now contains a script-src that allows our script. In order to have this work in the browser, the page above needs to be URL encoded with /.netlify%2fimages though. This gives us XSS! Netlify fixed the issue but the author found another bypass with an additional leading slash. For whatever reason, this has not been patched yet though. They fixed this by changing the types of files allowed on the CDN but left the parsing issue the same as before. Overall, a super interesting bug report! A mix of new technology with old bugs is fun to see.
Analysis Summary
# Vulnerability: CSP Bypass via URL Parser Confusion on Netlify Image CDN
## CVE Details
- **CVE ID:** Not Assigned (Vendor-specific disclosure)
- **CVSS Score:** Estimated 6.1 (Medium)
- **CWE:** CWE-444 (Inconsistent Interpretation of HTTP Requests), CWE-79 (Cross-site Scripting)
## Affected Systems
- **Products:** Netlify Image CDN
- **Versions:** All sites utilizing Netlify’s Image CDN optimization features prior to August 2024.
- **Configurations:** Websites using the `/.netlify/images?url=` endpoint for dynamic image transformation.
## Vulnerability Description
The vulnerability arises from a discrepancy between how the frontend Nginx reverse proxy and the backend application parse URL paths.
Netlify implemented a dynamic CSP based on the requested path. When a user accesses the standard image CDN path (`/.netlify/images`), Nginx injects a restrictive `script-src: 'none'` header to prevent XSS. However, by using "path normalization" tricks—specifically adding leading dots or extra slashes (e.g., `/./.netlify/images` or `//.netlify/images`)—an attacker can trick Nginx’s location matching logic.
Nginx fails to match these modified paths to the "block scripts" rule and serves a default, more permissive CSP. Meanwhile, the backend application ignores the extra characters, resolves the path to the image CDN, and fetches the attacker-controlled content specified in the `url` parameter.
## Exploitation
- **Status:** PoC available; reported to vendor and partially patched.
- **Complexity:** Medium (Requires finding an arbitrary file upload on a trusted domain or a bypass for content-type validation).
- **Attack Vector:** Network (Remote via Browser)
## Impact
- **Confidentiality:** Low (Can read data accessible to the user's session via XSS).
- **Integrity:** Medium (Can execute arbitrary JavaScript in the context of the vulnerable domain).
- **Availability:** None.
## Remediation
### Patches
- **Initial Fix:** Netlify updated the Image CDN to restrict allowed file types (Content-Type validation) to prevent the rendering of `text/html`.
- **Ongoing:** The researcher noted that while file-type restrictions were added, the underlying URL parsing confusion (the leading slash bypass) may remain in certain configurations.
### Workarounds
- Implement strict Content-Type sniffing headers (`X-Content-Type-Options: nosniff`).
- Hardcode restrictive CSP headers at the application level rather than relying solely on proxy-level path matching.
## Detection
- **Indicators of Compromise:** Requests to `/.netlify/images?url=` containing URL-encoded HTML files or scripts in the `url` parameter.
- **Detection methods:** Monitor web server logs for unusual path patterns such as `/./.netlify/` or `//.netlify/`.
## References
- [https://infosecwriteups.com/bypassing-csp-via-url-parser-confusions-xss-on-netlifys-image-cdn-755a27065fd9](https://infosecwriteups.com/bypassing-csp-via-url-parser-confusions-xss-on-netlifys-image-cdn-755a27065fd9)
- [https://docs.netlify.com/image-cdn/overview/](https://docs.netlify.com/image-cdn/overview/)
- [https://samcurry.net/universal-xss-on-netlifys-next-js-library/](https://samcurry.net/universal-xss-on-netlifys-next-js-library/)