GitHub Advisoryadvisory · npm
GHSA-92r3-m2mg-pj97 Vite XSS vulnerability in `server.transformIndexHtml` via URL payload
MEDIUM
Official page ↗
- Aliases
- CVE-2023-49293
- Ecosystem
- npm
- Packages
- vite
- Severity
- Moderate
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
- Weaknesses
- CWE-79
- Reviewed
- 2023-12-05
Affected packages
• npm vite: >= 4.4.0, < 4.4.12 → fixed in 4.4.12
• npm vite: >= 4.5.0, < 4.5.1 → fixed in 4.5.1
• npm vite: >= 5.0.0, < 5.0.5 → fixed in 5.0.5
Description
Summary
When Vite's HTML transformation is invoked manually via `server.transformIndexHtml`, the original request URL is passed in unmodified, and the `html` being transformed contains inline module scripts (`...`), it is possible to inject arbitrary HTML into the transformed output by supplying a malicious URL query string to `server.transformIndexHtml`.
Impact
Only apps using `appType: 'custom'` and using the default Vite HTML middleware are affected. The HTML entry must also contain an inline script. The attack requires a user to click on a malicious URL while running the dev server. Restricted files aren't exposed to the attacker.
Patches
Fixed in [email protected], [email protected], [email protected]
Details
Suppose `index.html` contains an inline module script:
```html
// Inline script
```
This script is transformed into a proxy script like
```html
```
due to Vite's HTML plugin:
https://github.com/vitejs/vite/blob/7fd7c6cebfcad34ae7021ebee28f97b1f28ef3f3/packages/vite/src/node/plugins/html.ts#L429-L465
When `appType: 'spa' | 'mpa'`, Vite serves HTML itself, and `htmlFallbackMiddleware` rewrites `req.url` to the canonical path of `index.html`,
https://github.com/vitejs/vite/blob/73ef074b80fa7252e0c46a37a2c94ba8cba46504/packages/vite/src/node/server/middlewares/htmlFallback.ts#L44-L47
so the `url` passed to `server.transformIndexHtml` is `/index.html`.
However, if `appType: 'custom'`, HTML is served manually, and if `server.transformIndexHtml` is called with the unmodified request URL (as the SSR docs suggest), then the path of the transformed `html-proxy` script varies with the request URL. For example, a request with path `/` produces
```html
```
It is possible to abuse this behavior by crafting a request URL to contain a malicious payload like
```
">alert('boom')
```
so a request to http://localhost:5173/?%22%3E%3C/script%3E%3Cscript%3Ealert(%27boom%27)%3C/script%3E produces HTML output like
```html
alert("boom")?html-proxy&index=0.js">
```
which demonstrates XSS.
PoC
• Example 1. Serving HTML from `vite dev` middleware with `appType: 'custom'`
• Go to https://stackblitz.com/edit/vitejs-vite-9xhma4?file=main.js&terminal=dev-html
• "Open in New Tab"
• Edit URL to set query string to `?%22%3E%3C/script%3E%3Cscript%3Ealert(%27boom%27)%3C/script%3E` and navigate
• Witness XSS:
•
• Example 2. Serving HTML from SSR-style Express server (Vite dev server runs in middleware mode):
• Go to https://stackblitz.com/edit/vitejs-vite-9xhma4?file=main.js&terminal=server
• (Same steps as above)
• Example 3. Plain `vite dev` (this shows that vanilla `vite dev` is _not_ vulnerable, provided `htmlFallbackMiddleware` is used)
• Go to https://stackblitz.com/edit/vitejs-vite-9xhma4?file=main.js&terminal=dev
• (Same steps as above)
• You should _not_ see the alert box in this case
Detailed Impact
This will probably predominantly affect development-mode SSR, where `vite.transformHtml` is called using the original `req.url`, per the docs:
https://github.com/vitejs/vite/blob/7fd7c6cebfcad34ae7021ebee28f97b1f28ef3f3/docs/guide/ssr.md?plain=1#L114-L126
However, since this vulnerability affects `server.transformIndexHtml`, the scope of impact may be higher to also include other ad-hoc calls to `server.transformIndexHtml` from outside of Vite's own codebase.
My best guess at bisecting which versions are vulnerable involves the following test script
```js
import fs from 'node:fs/promises';
import * as vite from 'vite';
const html = `
// Inline script
`;
const server = await vite.createServer({ appType: 'custom' });
const transformed = await server.transformIndexHtml('/?%22%3E%3C/script%3E%3Cscript%3Ealert(%27boom%27)%3C/script%3E', html);
console.log(transformed);
await server.close();
```
and using it I was able to narrow down to #13581. If this is correct, then vulnerable Vite versions are 4.4.0-beta.2 and higher (which includes 4.4.0).
Related
Alias · CVE (1)
- CVE-2023-49293 Vite is a website frontend framework. vulnerability
Weakness · CWE (1)
- CWE-79 Improper Neutralization of Input During Web Page Generation weakness