
Análisis en profundidad y PoC para CVE-2025-29927, una vulnerabilidad de omisión de autorización en middleware de Next.js a través del encabezado x-middleware-subrequest. Incluye plantillas de Nuclei y un script de escaneo masivo para pruebas.
Este documento presenta la investigación de la vulnerabilidad CVE-2025-29927, que afecta al mecanismo Middleware en el framework Next.js.
Next.js es un popular framework de código abierto de Vercel para desarrollar aplicaciones basadas en React. Admite renderizado del lado del servidor, generación estática y un sistema flexible de middleware, utilizado para enrutamiento, redirecciones, encabezados de seguridad y verificación de permisos de acceso.
En marzo de 2025 se identificó una vulnerabilidad crítica CVE-2025-29927, relacionada con el procesamiento del encabezado interno de subpeticiones.
La esencia del problema es la posibilidad de evitar las comprobaciones de autorización en aplicaciones donde el control de acceso está implementado precisamente en el middleware, mediante la inserción de un valor especial en el encabezado HTTP x-middleware-subrequest. Si la seguridad depende únicamente del middleware, un atacante puede acceder a rutas o datos protegidos.
Versiones afectadas de Next.js y correcciones (según fuentes públicas y materiales oficiales):
Las correcciones están disponibles en los lanzamientos 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3.
Next.js se usa ampliamente en producción; una vulnerabilidad que afecta la capa de middleware (a menudo utilizada para autenticación/autorización y políticas de seguridad) conlleva un alto riesgo práctico.
Analizar paso a paso la vulnerabilidad y documentar el ciclo completo de investigación:
Recopilación y estructuración de materiales.
Sistematizar las fuentes públicas sobre CVE-2025-29927; exponer la esencia del defecto, las condiciones de activación y las versiones/parches confirmados.
Determinación de CPE y condiciones de configuración.
Proporcionar una lista de CPE/versiones y describir las configuraciones en las que la vulnerabilidad es reproducible (por ejemplo, despliegue self-hosted y autorización a nivel de middleware).
Demostración segura.
Preparar una demo reproducible en un entorno de prueba (sin acciones destructivas) que confirme la evasión del middleware en versiones vulnerables.
Metodologías de verificación masiva.
Describir e implementar tres enfoques seguros:

Causa raíz. En Next.js, el encabezado interno x-middleware-subrequest se utiliza para rastrear subpeticiones internas y evitar recursividad en el middleware. En las ramas vulnerables, los clientes externos pueden inyectar este encabezado con un valor «esperado» y el runtime omite la ejecución del middleware, pasando la solicitud directamente al manejador de la ruta.
Rol del encabezado. El encabezado x-middleware-subrequest fue diseñado originalmente como un indicador interno de que la solicitud HTTP actual fue iniciada por el propio framework como una subpetición intermedia, no directamente por el usuario.
Es necesario para el correcto funcionamiento de los mecanismos internos de Next.js: además del enrutamiento, esta bandera ayuda a evitar la recursividad infinita «marcando» cada capa intermedia invocada.
Pero precisamente esta lógica generó un agujero de seguridad no intencionado: un cliente que agregue dicho encabezado puede hacer que el sistema trate su solicitud como interna y, por lo tanto, eluda las comprobaciones de autorización.
Evolución de la lógica.
Los registros actuales de NVD indican el producto Vercel Next.js con el software objetivo node.js. Para las ramas vulnerables se aplican las siguientes configuraciones (rangos de versiones definidos a nivel de configuración CPE en NVD):
| URI CPE | Rango de versiones vulnerable |
|---|---|
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 11.1.4 ≤ v < 12.3.5 |
Nota: en la descripción del CVE también se indica que, en general, «desde 11.1.4 hasta 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3» la vulnerabilidad es reproducible si se cumplen las condiciones siguientes.
next start, compilación con output: 'standalone') o cualquier entorno donde el middleware se ejecute en las solicitudes entrantes sin filtrado perimetral de encabezados internos.x-middleware-subrequest (por ejemplo, reglas WAF).x-middleware-subrequestPara evitar la recursividad infinita del middleware, el runtime genera y lee el encabezado interno:
: — se obtiene una matriz de «subpeticiones».NextResponse.next()),x-middleware-subrequest-id), vinculado a la sesión actual del proceso; si no coincide, el x-middleware-subrequest entrante se limpia en el lado del servidor.El atacante envía una solicitud HTTP a la aplicación Next.js objetivo, agregando el encabezado interno x-middleware-subrequest.
En el valor se indica la ruta al archivo del middleware — por ejemplo pages/_middleware, middleware o src/middleware.
El valor necesario depende de la versión de Next.js utilizada y de la estructura del proyecto.
Cuando dicha solicitud llega a la aplicación, la lógica interna del framework la interpreta como una subpetición interna y asume que la capa de middleware ya se ha ejecutado.
Como resultado, las comprobaciones de autenticación y autorización que normalmente ocurren en el middleware, se omiten en la práctica.
Importante: este material se proporciona exclusivamente para verificar el parche y modelar el riesgo en un entorno de prueba cerrado.
Cualquier verificación en infraestructura productiva solo es posible con el permiso del propietario de los recursos.
Next.js utiliza el encabezado interno x-middleware-subrequest para marcar subpeticiones internas y evitar la ejecución recursiva del middleware.
Al procesar la solicitud, el valor del encabezado se divide por el carácter : y se compara con el nombre del middleware actual.
En versiones nuevas se agregó un límite de profundidad (MAX_RECURSION_DEPTH, normalmente 5).
Si se cumple la condición (coincidencia de nombre o alcance del límite), Next.js omite el middleware y pasa la solicitud más adelante.
El atacante puede formar un valor correcto del encabezado, simulando una subpetición interna.
_middleware.*_middleware.js/ts y se ubicaban en el directorio pages/.x-middleware-subrequest: pages/_middlewaremiddleware.* raízmiddleware.js/ts./src.x-middleware-subrequest: middleware o x-middleware-subrequest: src/middleware:, y si la cantidad de repeticiones del nombre del middleware es ≥ 5,NextResponse.next().x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware o x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middlewareEn los lanzamientos 15.x, los desarrolladores rediseñaron la lógica para evitar llamadas recursivas infinitas.
Cuando en la ruta se activa el middleware (por ejemplo, /api/*), Next.js obtiene otra URL que también inicia el mismo middleware.
El framework cuenta el número de activaciones a través del encabezado x-middleware-subrequest.
Si el número de llamadas alcanza el umbral establecido (MAX_RECURSION_DEPTH, por defecto 5), las llamadas posteriores al middleware se bloquean.
El atacante puede aprovechar este comportamiento indicando de antemano en el encabezado un valor con la cantidad de repeticiones necesaria y, de este modo, alcanzar artificialmente el límite de profundidad — como resultado, el middleware se omite por completo.
para ramas antiguas, el valor del encabezado se elige según las variantes anteriores
GET /admin/dashboard HTTP/1.1
Host: <TEST-HOST>
User-Agent: <YOUR-LAB-CLIENT>
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
x-middleware-subrequest fue diseñado como un mecanismo interno para evitar la recursividad infinita.
Pero en las versiones vulnerables faltaba el filtrado de solicitudes externas: el cliente podía falsificar el encabezado,
cumplir la condición de omisión (coincidencia de nombre o alcance del límite de profundidad)
y acceder directamente al manejador de la ruta protegida.
git clone https://github.com/iteride/CVE-2025-29927.git
cd CVE-2025-29927/nextjs/
npm install
npm run dev
Pruebas:
(ejemplo basado en Next.js 15.2.2; para otras ramas elija el valor del encabezado según la tabla en la sección PoC)
curl -I http://localhost:3000
HTTP/1.1 307 Temporary Redirect
location: /403
Date: Mon, 22 Sep 2025 16:36:15 GMT
Connection: keep-alive
Keep-Alive: timeout=5
curl -I -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" http://localhost:3000
HTTP/1.1 200 OK
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Accept-Encoding
link: </_next/static/media/4cf2300e9c8272f7-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/media/93f479601ee12b01-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/css/app/layout.css?v=1758558988609>; rel=preload; as="style"
Cache-Control: no-store, must-revalidate
X-Powered-By: Next.js
Content-Type: text/html; charset=utf-8
Date: Mon, 22 Sep 2025 16:36:28 GMT
Connection: keep-alive
Keep-Alive: timeout=5
x-middleware-subrequest a nivel de WAF/proxy.X-Middleware-Subrequest especialmente formado.
⚠️ Atención: la plantilla está destinada únicamente para pruebas en objetivos propios o autorizados. Su uso en servicios de terceros sin consentimiento de los propietarios es ilegal.
python3 scan.py --targets-file TARGETS_FILE --paths-file PATHS_FILE --threads THREADS
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 13.0.0 ≤ v < 13.5.9 |
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 14.0.0 ≤ v < 14.2.25 |
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 15.0.0 ≤ v < 15.2.3 |