# CVE-2026-44574 / GHSA-492v-c6pp-mqqv — Dynamic-route param injection bypass | Field | Value | |----------------------|--------------------------------------------------------------------| | **Advisory** | GHSA-492v-c6pp-mqqv | | **CVE** | CVE-2026-44574 | | **Title** | Middleware/page mismatch via `nxtP*` / `nxtI*` internal-param injection | | **Severity** | High | | **CVSS (estimated)** | 7.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N) | | **CWE** | CWE-693 / CWE-444 (Protection-Mechanism Failure / Inconsistent interpretation) | | **Affected** | Next.js ≤ v16.2.4 | | **Patched** | v16.2.5 (commit `f1c11203d5` — see `patch.diff`) | > **Note on patch identity.** The diff stored as `patch.diff` is upstream > commit `f1c11203d5` ("Fix double-encoding of URL pathname parts in client > param parsing"). It is one of several commits in the v16.2.4..v16.2.5 > hardening cluster around dynamic-route param normalisation. The PoC below > targets the *user-observable* form of the issue: the ability to make > middleware see one pathname while the App Router renders a different > dynamic-segment value, by injecting Next's internal route-param search > params (`nxtP`, `nxtI`). ## 1. Summary Next.js represents matched dynamic segments and rewrites with reserved internal query parameters whose names start with `nxtP` (path) and `nxtI` (intercept). For example, `/admin/[slug]` matched against `/admin/profile` is internally tracked as `nxtPslug=profile`. When those parameters appear on an *incoming* request URL, the App Router trusts them when building `params` for the page render — but **middleware sees the original `request.nextUrl.pathname`**. The two views disagree, so a matcher that protects `/admin/[slug]` based on the canonical pathname can be made to render an arbitrary `slug` (or to make the page match a different route entirely) by injecting these params on a benign-looking URL. ## 2. Root cause * `URL.pathname.split('/')` on the client and `decodeURIComponent` + `encodeURIComponent` on the server produced **different** canonical forms, so middleware-derived values and page-derived values diverged for any path whose dynamic value contained characters such as `,`, `:`, or `%2F`. (See the canonicalisation patch in `patch.diff`, file `packages/next/src/client/route-params.ts`.) * Next never strips the `nxtP*` / `nxtI*` keys from incoming user requests, so a request like `/safe?nxtPslug=secret-admin-page` is forwarded to the page handler with a *server-controlled-looking* `params.slug = "secret-admin-page"` even though middleware saw `pathname = "/safe"`. The patch normalises the encoded pathname parts on the client so they round-trip identically with the server's `encodeURIComponent(decoded)` form, closing one variant of the smuggling primitive. ## 3. Exploit chain Threat model: Next.js app has * `app/admin/[slug]/page.tsx` — renders any page named `[slug]` under `/admin`. It is intended to be reachable only after middleware allows it. * `middleware.ts` — `if (req.nextUrl.pathname.startsWith('/admin')) requireAuth()`. 1. Attacker requests: GET /safe?nxtPslug=admin-only-page&__NEXT_PRIVATE_NO_MIDDLEWARE_RUN=1 Host: target Middleware sees `pathname = /safe` and lets it through (no `/admin` prefix). 2. The App Router router-server resolves the request, and because internal `nxtP` params are attached to the request meta, the page handler for `/admin/[slug]` is invoked with `params = { slug: "admin-only-page" }`. 3. The page renders content that should require auth. A second flavour exploits the encoded-slash mismatch: requesting `/admin/foo%252Fbar` (double-encoded) makes middleware see one segment value while the App Router decodes once and `[slug]` ends up as `foo/bar` — useful for matching different `if (slug === "...")` branches than middleware expected. ## 4. Impact * **Authorization bypass** — middleware-implemented access control is defeated for any page that uses dynamic segments. * **Cache poisoning** — different `nxtP*` values produce the same canonical URL in middleware/CDN caches but different rendered content. * **Param-name confusion** — apps that read `request.url` query params for feature flags can be tricked by `nxtP*` collisions. ## 5. Mitigation * Upgrade to **Next.js v16.2.5** or later. * In every protected layout/page, re-derive sensitive params from `params` (the resolved value), not from `request.url`. * At the CDN / WAF, drop/reject any inbound query whose key starts with `nxtP`, `nxtI`, or `__NEXT_`. ## Nuclei Template This advisory includes a Nuclei template: - `CVE-2026-44574.yaml` Run it with: ```bash nuclei -u -t poc/CVE-2026-44574_GHSA-492v-c6pp-mqqv/CVE-2026-44574.yaml ``` ## 6. Files in this PoC | File | Purpose | |---------------------------------------------------|---------| | `README.md` | This document | | `vulnerable-code.md` | Annotated pre-patch source | | `patch.diff` | Upstream fix (`f1c11203d5`) | | `exploit.sh` | Bash exploit (curl) | | `exploit.py` | Python exploit (urllib) | | `expected-output.txt` | Reference output | | `vulnerable-app/middleware.ts` | Vulnerable middleware (only checks pathname) | | `vulnerable-app/app/admin/[slug]/page.tsx` | Protected dynamic-segment page | | `vulnerable-app/app/safe/page.tsx` | Decoy/public page used as a host for the smuggle | | `vulnerable-app/setup.md` | How to wire into the harness |