# GHSA-267c-6grr-h53f — Middleware bypass via App Router segment-prefetch URLs | Field | Value | |----------------------|--------------------------------------------------------------------| | **Advisory** | GHSA-267c-6grr-h53f | | **CVE** | CVE-2026-44575 | | **Title** | Next.js proxy/middleware does not match App-Router segment-prefetch URL variants | | **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-863 / CWE-288 (Authorization Bypass) | | **Affected** | Next.js ≤ v16.2.4 (App Router with `middleware.ts` matchers) | | **Patched** | v16.2.5 (commit `d166096c39`) | | **Fix file** | `packages/next/src/build/analysis/get-page-static-info.ts` (`getMiddlewareMatchers`) | | **Patch PR** | vercel/next.js#89 / #96 | ## 1. Summary When a developer writes: ```ts // middleware.ts export const config = { matcher: '/dashboard/:path*' } ``` …Next.js compiles the `matcher` source into a regular expression that the edge router uses to decide *whether middleware runs* for a given request URL. Up to v16.2.4 the compiled regex only accepted the canonical pathname plus a single Pages-Router data variant `(.json)?`. **It did not include the App Router transport variants**: * `.rsc` — full-route RSC payload (used by client-side navigation/prefetch) * `.segments/$c$children/__PAGE__.segment.rsc` — segment-prefetch payload * `/index.rsc`, `/index.segments/...` — the root-route equivalents Because all of those URL forms resolve to the **same** App-Router page on the server, an attacker can request the `.rsc` / `.segments/*.segment.rsc` form to fetch the page output (HTML or RSC payload) *without ever invoking the middleware that performs the auth check on the canonical path*. ## 2. Root cause File: `packages/next/src/build/analysis/get-page-static-info.ts` Function: `getMiddlewareMatchers` Pre-patch (vulnerable) suffix construction: ```ts source = `/:nextData(_next/data/[^/]{1,})?${source}${ isRoot ? `(${nextConfig.i18n ? '|\\.json|' : ''}/?index|/?index\\.json)?` : '{(\\.json)}?' // ← only matches the Pages-Router data variant }` ``` The trailing `{(\\.json)}?` group means the matcher regex only recognises: * `/dashboard` * `/_next/data//dashboard.json` …but NOT: * `/dashboard.rsc` * `/dashboard.segments/$c$children/__PAGE__.segment.rsc` Post-patch the suffix is widened (see `patch.diff`): ```ts const APP_ROUTE_TRANSPORT_SUFFIX_MATCHER = `${escapeStringRegexp(RSC_SUFFIX)}|${escapeStringRegexp(RSC_SEGMENTS_DIR_SUFFIX)}/.+${escapeStringRegexp(RSC_SEGMENT_SUFFIX)}` const MIDDLEWARE_DATA_SUFFIX_MATCHER = `\\.json|${APP_ROUTE_TRANSPORT_SUFFIX_MATCHER}` // … source = `${OPTIONAL_MIDDLEWARE_NEXT_DATA_PREFIX}${source}${sourceSuffix}` ``` So the regex now matches `.rsc` and `.segments/.../.segment.rsc` as alternative suffixes for the same logical path. A companion patch (`0dd94836a8` — "fix: add explicit checks for RSC header") hardens the related RSC-header recognition (`isRSCRequestHeader`) and is part of the same hardening cluster. ## 3. Exploit chain Threat model: Next.js application protects `/dashboard` via `middleware.ts` that redirects unauthenticated users to `/login`. 1. Unauthenticated attacker requests **`GET /dashboard.rsc`** (or `GET /dashboard.segments/$c$children/__PAGE__.segment.rsc`) with header `RSC: 1` and optionally `Next-Router-Prefetch: 1`. 2. The edge router compiles the middleware matcher from `/dashboard/:path*` into a regex that does not match `.rsc`. 3. **Middleware is skipped entirely**. 4. The App Router renders the protected page and returns the RSC flight payload (`Content-Type: text/x-component`) — which contains the same server-rendered tree the HTML page would render, including any sensitive server-side data. 5. The attacker parses the RSC payload to extract DOM text, links, JSON embedded in the tree, etc. The same trick works for the root route by requesting `/index.rsc` instead of `/`. ## 4. Impact * **Authorization bypass** — protected App-Router pages are reachable without going through middleware-implemented auth/locale/feature-flag checks. * **Information disclosure** — the RSC payload often contains serialised props, environment-derived strings, and the full subtree the authenticated user would have seen. * **Defense-in-depth break** — many apps rely on middleware as the *only* layer of authentication for App Router pages. ## 5. Mitigation * Upgrade to **Next.js v16.2.5** or later. The fix widens the matcher regex. * If immediate upgrade is not possible, perform an additional auth check inside the protected page/layout (e.g. read cookies in a Server Component) so the RSC variant cannot bypass the check. * Strip / 404 RSC requests at the CDN/WAF for protected paths. ## Nuclei Template This advisory includes a Nuclei template: - `CVE-2026-44575.yaml` Run it with: ```bash nuclei -u -t poc/CVE-2026-44575_GHSA-267c-6grr-h53f/CVE-2026-44575.yaml ``` ## 6. Files in this PoC | File | Purpose | |----------------------------------|---------| | `README.md` | This document | | `vulnerable-code.md` | Annotated pre-patch source | | `patch.diff` | Upstream fix (`d166096c39`) | | `exploit.sh` | Bash exploit (curl) | | `exploit.py` | Python exploit (requests) | | `expected-output.txt` | Reference output | | `vulnerable-app/middleware.ts` | Vulnerable middleware (matches only canonical path) | | `vulnerable-app/app/dashboard/page.tsx` | Protected App Router page | | `vulnerable-app/setup.md` | How to wire into the harness |