
Este es un escáner de CVE-2025-29927.
Este es un escáner de nivel profesional diseñado para detectar la vulnerabilidad de omisión de middleware CVE-2025-29927 en aplicaciones Next.js.
X-Middleware-Subrequest manipulados para omitir el middleware de Next.jspip install -r requirements.txt playwright install
### Ejecutar el escáner```bash
python main.py --domain https://example.com --threads 10 --timeout 10 --save
python main.py --help
| Opción | Descripción |
|----------------|----------------------------------------------------|
| `--domain` | URL base del sitio objetivo (obligatorio) |
| `--user-agent` | User-agent personalizado (predeterminado: cadena de Chrome) |
| `--timeout` | Tiempo de espera de la solicitud (predeterminado: 10 segundos) |
| `--proxy` | Dirección del proxy (opcional) |
| `--save` | Guardar resultados en `results.txt` |
| `--threads` | Número de hilos (predeterminado: 10) |
| `--wordlist` | Lista de palabras incluye rutas comunes |
---
## 🐳 Uso de Docker
### Construir imagen de Docker```bash
docker build -t cve-scanner .
docker run -it --rm cve-scanner --domain https://example.com --save
---
## ⚙️ GitHub Actions
Este proyecto incluye un flujo de trabajo de GitHub Actions para probar la configuración en cada push. Dicho flujo:
- Instala las dependencias
- Instala los navegadores de Playwright
- Ejecuta una comprobación de `--help`
Consulta `.github/workflows/python.yml`.
---
## 🧱 Estructura```
.
├── main.py # Entry point
├── config.py # CLI parser
├── crawler.py # Playwright crawler
├── scanner.py # Multi-threaded vulnerability testing
├── requirements.txt
├── Dockerfile
└── .github/workflows
Resumen de la vulnerabilidad CVE-2025-29927
CVE-2025-29927 es una falla de seguridad crítica en Next.js que permite a los atacantes eludir la autenticación y la autorización basadas en middleware. Al incluir un encabezado interno especial (X-Middleware-Subrequest) en las solicitudes HTTP, un atacante puede engañar al servidor Next.js para que omita la ejecución del middleware, obteniendo así acceso a rutas protegidas. En la práctica, una solicitud que normalmente sería bloqueada por el middleware de autenticación (por ejemplo, devolviendo un 401/403 o redirigiendo al inicio de sesión) se procesa normalmente si este encabezado está presente, eludiendo eficazmente los controles de seguridad. Esta vulnerabilidad afecta a las versiones de Next.js 11.1.4 a 15.2.2, y se insta a los administradores a aplicar parches o implementar mitigaciones (como eliminar este encabezado en los proxies) para proteger sus aplicaciones.
Detectar esta vulnerabilidad en una aplicación web requiere descubrir los endpoints internos y probarlos con el encabezado malicioso para ver si es posible el acceso no autorizado. A continuación se presenta un plan de diseño para un script avanzado en Python que rastreará un sitio web objetivo (con soporte completo de JavaScript) y escaneará en busca de CVE-2025-29927, cumpliendo todos los requisitos especificados.
Para cumplir con el requisito de un rastreo profundo que incluya contenido renderizado por JavaScript, usaremos Playwright (preferido sobre Selenium por su velocidad y su API moderna). Playwright es una potente librería de automatización de navegadores headless que puede manejar aplicaciones web dinámicas y frameworks JS modernos. En comparación con Selenium, Playwright ofrece una API más moderna (construida sobre el Chrome DevTools Protocol) y admite operación tanto síncrona como asíncrona, lo que puede ofrecer un mejor rendimiento para nuestro caso de uso. Las librerías clave y sus instrucciones de instalación incluyen:
playwright – para la automatización de navegadores headless (para cargar SPAs o páginas que requieren JS). (Instalación: pip install playwright y ejecutar playwright install para obtener los binarios del navegador).
requests o httpx – para enviar solicitudes HTTP durante la fase de escaneo. Podemos usar requests por simplicidad o httpx/aiohttp para soporte asíncrono. (Instalación: pip install requests o pip install httpx).
bs4 (BeautifulSoup) – para parsear HTML y extraer enlaces cuando sea necesario. Playwright puede consultar el DOM directamente, pero usar BeautifulSoup sobre el contenido HTML de la página es sencillo para encontrar etiquetas de anclaje. (Instalación: pip install beautifulsoup4).
concurrent.futures (integrado) o asyncio – para implementar concurrencia. Para el multihilo, se usará concurrent.futures.ThreadPoolExecutor de Python (sin instalación adicional). Si se usa un enfoque asíncrono, se puede usar asyncio de Python con para solicitudes en paralelo.
Justificación: Playwright se elige por su capacidad para scrapear contenido dinámico sin complejidades pesadas. “Con Playwright podemos automatizar navegadores headless... para navegar por la web igual que un humano, lo que lo hace excelente para scrapear sitios web dinámicos impulsados por JavaScript”. Esto garantiza que nuestro rastreador pueda ver enlaces o elementos de interfaz que son generados por scripts (que un rastreador simple basado en requests pasaría por alto).
El módulo rastreador usará Playwright en modo headless para realizar un rastreo profundo del sitio objetivo. El objetivo es descubrir rutas internas (endpoints) para probar, incluidas aquellas que solo se revelan después de la ejecución de JS. Puntos clave de diseño del rastreador:
Navegación con navegador headless: Lanzar una instancia de navegador (por ejemplo, Chromium) en modo headless a través de Playwright. Usar un Browser Context con un User-Agent personalizado si el usuario lo especifica (más sobre esto en la siguiente sección). Por ejemplo, podemos crear un contexto con browser.new_context(user_agent=<user_agent_string>) para emular el User-Agent elegido. Si hay un proxy configurado, aplicarlo al lanzar (Playwright permite configurar un servidor proxy al lanzar el navegador o el contexto).
Estrategia de rastreo recursivo: Comenzar desde una URL base determinada (semilla). Usar page.goto(base_url, timeout=<T>) para cargar la página (timeout configurable). Esperar a que la red esté inactiva o aplicar un breve retraso para permitir que el contenido dinámico se cargue si es necesario. Luego extraer los enlaces. Podemos extraer los enlaces de dos maneras:
links = page.evaluate("Array.from(document.querySelectorAll('a[href]'), a => a.href)"), o<a href>.Filtrado de enlaces: Filtrar los enlaces que no estén dentro del dominio objetivo (para permanecer en el ámbito interno). También ignorar las URL de archivos estáticos, como imágenes, CSS, JS, etc. Por ejemplo, omitir cualquier URL con extensiones de archivo como .css, .js, .jpg, .png, .gif, .svg, .woff, etc. Un enfoque práctico (inspirado en la plantilla de ProjectDiscovery) es ignorar cualquier ruta que contenga un "punto" después de la barra inicial. Ellos extrajeron endpoints con un patrón de regex href=['"](https://github.com/houmanpashaei/cve-2025-29927/blob/HEAD/%5C/%5B%5E.%5C%22%27%5D+)['"] – esto captura rutas internas que no contienen un punto (omitiendo así los recursos estáticos). Implementaremos una lógica similar en el código para evitar poner en cola recursos estáticos o enlaces externos.
Seguimiento y control de profundidad: Mantener un conjunto de URL visitadas para evitar bucles infinitos o repeticiones. Usar una cola (FIFO) para el recorrido BFS del grafo de enlaces del sitio. Opcionalmente, permitir al usuario especificar un límite de profundidad de rastreo o un número máximo de páginas a visitar para evitar que se ejecute para siempre en sitios grandes.
Contenido renderizado por JavaScript: Debido a que usamos un navegador real, incluso los enlaces que los scripts añaden al DOM (por ejemplo, una aplicación React que renderiza un menú después de obtener datos) serán visibles para nuestro rastreador. Deberíamos considerar hacer clic o interactuar si es necesario (por ejemplo, si ciertas páginas solo se cargan después de una acción del usuario). Sin embargo, para mantener las cosas simples y rápidas, el diseño inicial se centrará en recopilar los href de los en cada página cargada. Podemos mejorarlo más adelante para manejar cosas como el scroll infinito o el contenido tras clics si la aplicación objetivo lo requiere.
Eficiencia: Playwright admite ejecutar múltiples páginas/pestañas en paralelo usando su API asíncrona. Podríamos instanciar múltiples páginas con asyncio.gather para obtener varios enlaces de forma concurrente. Para una implementación inicial, un enfoque más simple es rastrear secuencialmente (que es más fácil de implementar) y confiar en el escaneo multihilo para el rendimiento. Si es necesario, una optimización avanzada podría involucrar un rastreo asíncrono (usando async with async_playwright() y esperando múltiples llamadas a page.goto). Pero dado que la automatización del navegador consume más recursos, un enfoque prudente es mantener quizás una o unas pocas páginas del navegador a la vez para evitar abrumar al sistema.
El script presentará un menú de configuración fácil de usar al inicio, permitiendo al usuario personalizar los parámetros de escaneo o aceptar los valores predeterminados. Esto podría hacerse mediante un menú interactivo de consola (usando prompts de input()) o mediante argumentos de línea de comandos (usando argparse para una experiencia CLI más profesional). Las opciones incluyen:
User-Agent personalizado: El usuario puede especificar una cadena de User-Agent personalizada para que el rastreador y el escáner la usen. Esto se aplicará al contexto del navegador de Playwright y a cualquier solicitud HTTP directa. Usar un User-Agent no predeterminado puede ayudar a evitar la detección trivial de bots. (Por defecto, Playwright podría usar algo identificable; podemos sobrescribirlo fácilmente como se mostró arriba). Por ejemplo, el usuario podría ingresar una cadena que lo identifique como Chrome en Windows, que pasamos a la creación del contexto de Playwright.
Tiempo de espera (timeout) de solicitud: El usuario puede establecer un tiempo de espera (en segundos) para las cargas de página y las solicitudes HTTP. Esto evita que el escáner se cuelgue demasiado tiempo en endpoints que no responden. Aplicaremos esta configuración en para el rastreo y en las solicitudes (por ejemplo, ) para el escaneo.
httpx(Opcional) argparse – para parsear argumentos de línea de comandos si queremos una interfaz CLI en lugar de un menú interactivo. (módulo integrado)
(Opcional) rich o colorama – para salida de consola con colores o formato, para mejorar la legibilidad. (Instalación: pip install rich o pip install colorama).
page.goto(timeout=...)requests.get(timeout=...)Configuración de proxy: Si el usuario quiere enrutar el tráfico a través de un proxy (por anonimato o para alcanzar hosts internos), puede ingresar la URL del proxy (y las credenciales si es necesario). El script configurará el navegador de Playwright para usar este proxy al lanzarlo (por ejemplo, browser.launch(proxy={"server": "http://<proxy_host>:<port>", "username": "...", "password": "..."}) como se muestra en los ejemplos). De manera similar, para las solicitudes, estableceremos el parámetro proxies (o las variables de entorno) en consecuencia.
Salida a archivo: El menú preguntará si el usuario quiere guardar los resultados en un archivo (por ejemplo, results.txt). Si es así, el script escribirá cualquier endpoint vulnerable descubierto y los detalles en este archivo, además de imprimirlos en pantalla. Si no, los resultados simplemente se imprimirán en stdout. (Aún así, posiblemente registremos todas las rutas escaneadas en un registro verbose si es necesario, pero el archivo registraría específicamente los positivos o el informe completo según la preferencia del usuario).
Otras opciones: Podemos incluir conmutadores como “Modo verbose” para el registro de depuración, o “Profundidad/páginas máximas de rastreo” si es necesario. Estos pueden ayudar al usuario a ajustar finamente el escaneo. Para el alcance inicial, las cuatro opciones principales anteriores son suficientes.
El sistema de menús se implementará en un módulo de configuración/setup dedicado. Esto podría ser simplemente una función que imprime prompts y recopila la entrada, con valores predeterminados sensatos si el usuario presiona Enter (por ejemplo, user-agent predeterminado a uno estándar, timeout predeterminado = 10 segundos, sin proxy, sin salida a archivo). Esto mantiene la interacción clara y permite que el script se ejecute también de forma no interactiva (si más adelante agregamos argumentos de línea de comandos, podemos omitir los prompts interactivos proporcionando toda la configuración necesaria mediante args).
El rendimiento es crucial para un escáner, especialmente si se encuentran muchos endpoints. El script empleará concurrencia para la velocidad, ya sea mediante multihilo o asyncio (o una combinación):
Escaneo multihilo: Dado que el escaneo de las rutas descubiertas (enviar solicitudes HTTP con encabezados) es una tarea limitada por E/S (I/O-bound), podemos usar hilos de Python de forma segura para paralelizarla. Las operaciones de E/S liberan el Global Interpreter Lock, lo que permite que múltiples hilos avancen de forma concurrente en las solicitudes de red. Usando concurrent.futures.ThreadPoolExecutor, podemos tener un grupo de hilos de trabajo, cada uno manejando un subconjunto de las tareas de escaneo. Esto puede acelerar drásticamente el proceso: por ejemplo, ejecutar 5 hilos en paralelo podría reducir el tiempo de escaneo aproximadamente por un factor de 5, como se muestra en otros contextos de web scraping. Permitiremos que el número de hilos sea configurable o elegiremos un valor predeterminado sensato (como 10 hilos) que equilibre la velocidad y la carga del servidor. Cada hilo tomará URL de una cola compartida de endpoints a probar.
Alternativa con asyncio: Alternativamente, se puede usar un enfoque asíncrono, especialmente si se usa Playwright en modo async o httpx para las solicitudes HTTP. Podríamos await múltiples solicitudes simultáneamente. Por ejemplo, httpx.AsyncClient puede enviar muchas solicitudes de forma concurrente y recopilar los resultados. Este enfoque evita la sobrecarga de los hilos y puede ser muy eficiente para un gran número de endpoints. Sin embargo, mezclar asyncio con Playwright (que a su vez se puede usar de forma asíncrona) podría complicar las cosas. Una solución pragmática es usar threading para la fase de escaneo HTTP (ya que el rastreo con Playwright podría ser más fácil de gestionar en modo síncrono).
Rastreo concurrente: También deberíamos considerar paralelizar el rastreo si el sitio es grande. Playwright puede abrir múltiples páginas a la vez usando un contexto async. Podríamos implementar una concurrencia limitada (por ejemplo, 2-3 páginas a la vez) para el rastreo. Por ejemplo, a medida que extraemos nuevas URL, podríamos lanzar una nueva Page para cada una si usamos asyncio. Esto puede ser una optimización avanzada si es necesario. Inicialmente, un rastreo de un solo hilo es más simple y adecuado para sitios de tamaño moderado, pero el diseño puede señalarlo como un punto de mejora.
Seguridad de hilos (thread-safety): Garantizaremos el manejo seguro de los datos compartidos entre hilos. La lista de URL a escanear se puede procesar con ThreadPoolExecutor.map por simplicidad, o podemos usar una cola segura para hilos (queue.Queue de Python) y hacer que los hilos tomen elementos de ella hasta que esté vacía. El conjunto visited para el rastreo solo es accedido por el rastreador (un solo hilo, a menos que hagamos rastreo concurrente). Los hilos del escáner solo leerán de su lista de URL (sin modificar estructuras compartidas, excepto quizás el registro de resultados, que podemos proteger con un bloqueo o simplemente recopilar en una lista segura para hilos).
Límite de velocidad (rate limiting) y cortesía: Dado que esta es una herramienta de pruebas de seguridad, la velocidad es una prioridad, pero aún así podríamos querer evitar sobrecargar el objetivo. Se puede aconsejar al usuario establecer un número razonable de hilos. También podemos implementar un pequeño retraso o usar semáforos para limitar la concurrencia si es necesario. Por ejemplo, podríamos no lanzar todos los hilos a la vez si la red del usuario o el servidor pudieran saturarse. En un escenario avanzado, un enfoque asíncrono podría usar un semáforo para permitir, digamos, 5 solicitudes concurrentes a la vez. Estos detalles se pueden ajustar en función de las pruebas de rendimiento del script.
En resumen, la concurrencia se aplicará principalmente a la fase de escaneo para probar múltiples endpoints en paralelo. Esto hace que el escáner sea mucho más rápido sin sacrificar mucha precisión (ya que cada solicitud es independiente). Como señala una referencia, “El multihilo con concurrent.futures puede dar un impulso significativo aquí. Podemos ejecutar tareas de E/S de forma concurrente en múltiples hilos y ver una gran aceleración”. El multihilo es adecuado aquí porque las tareas limitadas por la red se benefician de él incluso en Python.
El corazón del script es el módulo de escaneo, que toma la lista de endpoints descubiertos (rutas) y verifica cada uno en busca de señales de la vulnerabilidad CVE-2025-29927. El proceso para cada endpoint será:
x-middleware-rewrite, x-middleware-next o x-middleware-redirect, eso sugiere que esta ruta está protegida por middleware. También comprobamos si el estado no es 200 (lo que significa que el acceso fue denegado o redirigido), ya que esos son los que probablemente se puedan eludir. (Si el estado ya es 200 y el contenido se carga normalmente, es una página pública o la vulnerabilidad no aplica; podríamos probarla de todos modos, pero el interés real está en las páginas protegidas).X-Middleware-Subrequest. Probaremos una variedad de valores de encabezado para asegurar la detección en todas las versiones de Next.js:Un valor genérico como "1" o "true" (algunas fuentes implican que simplemente establecer el encabezado a cualquier valor activa la omisión).
El payload específico usado en exploits públicos, por ejemplo "middleware:middleware:middleware:middleware:middleware" (five repetitions of "middleware"). Se sabe que esto induce la omisión en las versiones más recientes (13+). Incluiremos exactamente este valor.
El payload alternativo para proyectos que usan el directorio /src, por ejemplo "src/middleware:src/middleware:src/middleware:src/middleware:src/middleware"
Opcionalmente, valores de un solo segmento como "middleware" o "src/middleware" por completitud (las versiones antiguas de Next.js podrían usar un archivo _middleware en el directorio pages, con un payload necesario ligeramente diferente, pero los payloads de múltiples segmentos anteriores cubren en gran medida los casos conocidos).
Cada una de estas solicitudes se realizará con el encabezado personalizado establecido. También nos aseguramos de usar el mismo método (GET) e incluir cualquier encabezado de la línea base que pudiera ser necesario (como cookies o tokens de autenticación si el usuario proporcionó alguno para un escaneo con sesión iniciada, aunque normalmente escaneamos sin autenticación).
Si la línea base fue un error o una redirección (por ejemplo, 401 No autorizado, 403 Prohibido, o una redirección al inicio de sesión) y una de las respuestas con el encabezado inyectado es 200 OK con un cuerpo significativamente más grande (o que indique de otra manera que la página se cargó), eso es un fuerte indicador de vulnerabilidad. Por ejemplo, si /admin devolviera 403 normalmente, pero con el encabezado devuelve 200 y contiene el HTML del panel de administración, lo marcamos.
En algunos casos, la diferencia podría ser un 302 frente a un 200, o un 404 frente a un 200. Consideraremos un cambio de código de estado de no-200 a 200 como una señal probable. Además, si el estado permanece en 200 pero la longitud del contenido cambia drásticamente, eso podría indicar que el encabezado alteró el comportamiento (menos común para este fallo en particular, pero una posibilidad si la página normalmente entregaba una cosa y con el encabezado entregaba otra).
Implementaremos comprobaciones como: if base_status_code != 200 and test_status_code == 200: (y quizás también asegurar test_body_length > base_body_length o que contenga alguna palabra clave de autenticación) y luego marcar como vulnerable. Si la línea base fue una redirección (por ejemplo, 307 a /login) y la prueba devuelve 200, también marcar. En esencia, “¿el acceso estaba antes denegado pero ahora permitido?”.
Si el estado de la respuesta con el encabezado es 404 o 500 cuando la línea base fue una redirección, esto podría ser el escenario de envenenamiento de caché (omitir la redirección del middleware causando un 404 en el origen). Ese escenario es un poco más difícil de detectar con una sola solicitud, pero la presencia de un 404 con el encabezado cuando la línea base fue una redirección también podría notarse (aunque no es una omisión de autenticación, sigue siendo un efecto de la vulnerabilidad). Sin embargo, nuestro enfoque está en detectar una omisión de autenticación (acceso 200 OK).
results.txt si el usuario optó por guardar los resultados. Deberíamos formatear esto claramente, por ejemplo:[*] /admin -> baseline 403, with X-Middleware-Subrequest (payload X) got 200 [VULNERABLE]
También podemos imprimir algo como la longitud de la respuesta o un fragmento de la respuesta para confirmar (quizás solo la longitud por brevedad, por ejemplo, "len: 0 -> 10240 bytes"). Si se probaron múltiples payloads, podríamos listar cuáles tuvieron éxito.
Si el sitio parece no ser una aplicación Next.js en absoluto (por ejemplo, no encontramos ningún /_next/static/ en la página de inicio, que es una señal reveladora), podríamos mostrar una nota: “No se encontraron indicadores de Next.js, el objetivo podría no estar usando Next.js – probablemente no sea vulnerable”. Pero aún podemos proceder de forma genérica, ya que la verificación de Next.js es una optimización más que una necesidad.Esta lógica estará encapsulada limpiamente. Por ejemplo, podríamos tener una función scan_endpoint(url, session, header_payloads) que devuelva un objeto de resultado o dict con si es vulnerable y detalles. Incorporaremos comprobaciones robustas para evitar falsos positivos. Específicamente, exigir un cambio de código de estado a 200 (u otra evidencia clara) ayuda a garantizar que solo marquemos bypass reales. Como se señala en el análisis de ProjectDiscovery, el escáner comprueba que la respuesta tenga código de estado 200 cuando se incluye el encabezado especial para confirmar la vulnerabilidad.
La salida del script debe ser fácil de leer e interpretar, además de poder guardarse opcionalmente en un archivo. Formatearemos la salida de consola con encabezados claros e indentación donde corresponda. Algunas consideraciones:
Después del escaneo, imprime un resumen de los hallazgos. Por ejemplo: “Escaneo completo: 3 endpoints vulnerables encontrados (de 45 probados).” Luego lista los endpoints vulnerables con detalles.
Usa un formato consistente para cada línea de resultado, como se mostró arriba, posiblemente con etiquetas [VULNERABLE] para llamar la atención. Si se usa una librería como rich, incluso podríamos codificar por colores “VULNERABLE” en rojo o amarillo. Incluso sin librerías adicionales, podemos usar códigos ANSI mediante colorama para resaltar, o simplemente texto en mayúsculas.
Si no se encuentran vulnerabilidades, dilo explícitamente: “No se detectaron vulnerabilidades para CVE-2025-29927.”
Si los resultados se van a guardar, asegúrate de que se escriban en un formato similar al del archivo. Posiblemente de una manera un poco más detallada o en CSV para uso programático, pero como el usuario mencionó específicamente un archivo de texto, probablemente simplemente escribiremos las mismas líneas en results.txt.
Además, cualquier error crítico o excepción encontrada (como no poder cargar cierta página) puede reportarse en la salida de manera elegante (en lugar de un stack trace). Podemos capturar excepciones e imprimir una advertencia de una línea por cada URL fallida: p. ej., “Tiempo de espera agotado al cargar /blog (omitido)”. De esta manera, el usuario sabe si algunas rutas no fueron probadas.
Durante la ejecución, podríamos mostrar un spinner o progreso (para ejecuciones largas) o al menos imprimir qué página se está rastreando o qué endpoint se está probando, si el modo verbose está activado. Para una salida más limpia, podríamos mostrar solo los casos vulnerables descubiertos al final, pero un registro continuo (quizás escribiendo en un archivo de log separado) puede ayudar con la transparencia.
Dado el énfasis en un formato claro, usar viñetas o un diseño de tabla podría ayudar al imprimir múltiples resultados:
Podríamos tabular como: Endpoint | Base Status | Base Length | Bypass Status | Bypass Length | HeaderValueUsed | Result
Sin embargo, una forma de oración simple podría ser más legible para una amplia gama de usuarios. Nos aseguraremos de que cada resultado esté en una nueva línea y etiquetado claramente.
Al proporcionar tanto salida en pantalla como un guardado opcional en archivo, la herramienta es útil tanto para uso interactivo como para escaneo automatizado (donde el usuario puede revisar el archivo posteriormente o integrarlo en informes).
Para hacer que el script sea mantenible y de nivel profesional, organizaremos el código en módulos, cada uno manejando un aspecto distinto de la funcionalidad. Una posible estructura de proyecto:
crawler.py: Contiene la lógica de rastreo usando Playwright. Tendrá funciones como crawl_site(start_url, config) -> List[str] que devuelve una lista de rutas internas descubiertas. Este módulo manejará el lanzamiento del navegador, la obtención de páginas, la extracción de enlaces y la aplicación de filtros (dominio, exclusión de archivos estáticos). También podría albergar lógica auxiliar para normalizar URLs (p. ej., eliminar fragmentos de URL, manejar rutas relativas mediante urllib.parse.urljoin).
scanner.py: Contiene la lógica de escaneo de la vulnerabilidad. Incluirá funciones como scan_paths(url_list, config) -> List[ScanResult]. Esto gestionará la creación de peticiones HTTP (usando requests.Session o un cliente httpx), la aplicación de encabezados, la comparación de respuestas y la recopilación de resultados. Si se usa multihilo, este módulo crearía el ThreadPool y gestionaría las tareas. Podría definir una pequeña clase de datos ScanResult para contener información sobre cada ruta (path, vulnerable: bool, details).
config.py (o settings.py): Contiene el código para el menú de usuario y la configuración. Por ejemplo, una función get_user_config() que interactúa con el usuario y devuelve un objeto/diccionario de configuración con todos los ajustes elegidos (user_agent, timeout, proxy, flag output_file, etc.). Si se usan argumentos de CLI, este módulo podría alternativamente analizar argparse.ArgumentParser. En esencia, esta parte aísla toda la entrada del usuario y el manejo de configuración.
utils.py: Funciones de utilidad, p. ej., para imprimir banners, formatear cadenas de salida, manejar salida con colores, o un helper común como is_static_resource(url) (para comprobar si una URL probablemente apunta a un archivo estático). También podría incluir una función para apagado elegante (para ser llamada en SIGINT).
main.py: El script de punto de entrada que une todo. Hará lo siguiente:
config.py).main podría simplemente estar al final de un solo archivo, pero por limpieza, separar es mejor.Cada módulo estará diseñado para ser modular y reutilizable. Por ejemplo, uno podría reutilizar crawler.py para obtener enlaces del sitio para otros propósitos, o reutilizar scanner.py para probar esta vulnerabilidad en una lista dada de URLs (incluso sin rastreo).
Manejo de Excepciones y Apagado Elegante: Implementaremos un manejo robusto de excepciones:
Rodea las operaciones de red con try/except (captura timeouts, errores de conexión, etc.). Si el rastreo de una página falla, regístralo y continúa con las demás. Si una petición de escaneo falla (p. ej., error de proxy), marca ese endpoint como error pero continúa escaneando el resto.
Usa bloques finally o administradores de contexto para asegurar que los recursos se limpien. Por ejemplo, usa el contexto async_playwright() o asegúrate de que se llame a browser.close() al final del rastreo. De manera similar, asegúrate de que los manejadores de archivo se cierren después de escribir.
Maneja KeyboardInterrupt (Ctrl+C): Podemos capturar el KeyboardInterrupt en el bucle principal e iniciar un apagado elegante – p. ej., imprimir “Deteniendo, limpiando…”, apagar los hilos (quizás usando ThreadPoolExecutor.shutdown(wait=False) para detener el lanzamiento de nuevas tareas) y cerrar el navegador. Esto previene procesos huérfanos o archivos bloqueados si el usuario aborta.
Usa logging para mensajes de depuración (quizás mediante la librería logging de Python). En una herramienta profesional, tendrías niveles de logging; p. ej., los logs de depuración podrían incluir cada petición realizada, mientras que el nivel info solo muestra progreso de alto nivel. El usuario podría establecer un flag verbose para alternar esto. Por defecto, podríamos registrar información mínima para no abrumar la salida.
Calidad del Código: Nos adheriremos a las mejores prácticas de codificación:
Seguir las guías de estilo PEP8 para la legibilidad.
Usar nombres significativos para funciones y variables.
Añadir docstrings a las funciones explicando su propósito y uso.
Usar type hints en las firmas de funciones (anotaciones de tipo de Python 3) para hacer el código más fácil de entender y detectar problemas de tipo temprano.
Modularizar constantes (como la lista de payloads de encabezado, listas de extensiones de archivos estáticos a ignorar, etc.) en la parte superior o en una configuración, para que puedan actualizarse fácilmente. Por ejemplo, HEADER_PAYLOADS = ["middleware:middleware:...","src/middleware:..."] etc., definidas en un solo lugar.
Posiblemente incluir pruebas unitarias para algunas funciones auxiliares (si esto fuera un proyecto más grande, aunque para una herramienta de un solo script podría omitirse; aun así, diseñar pensando en la capacidad de prueba es beneficioso).
Mejoras de Nivel Profesional: Para hacer que el script sea más robusto y listo para producción, podemos considerar además:
Soporte de autenticación: Permitir que el usuario proporcione cookies o credenciales si quiere escanear una sección autenticada del sitio (aunque la vulnerabilidad trata sobre evadir la autenticación, puede haber escenarios donde necesites iniciar sesión primero para alcanzar ciertos enlaces y luego probar la evasión en ellos – aunque el bypass presumiblemente funciona sin autenticación válida, esto podría ayudar a rastrear enlaces profundos que no son públicos).
Archivo de configuración: En lugar de (o además de) la entrada interactiva, permitir leer opciones desde un archivo de configuración o variables de entorno, lo cual es útil para despliegues automatizados del escáner.
Formatos de salida: Proporcionar salida en múltiples formatos como JSON o CSV para integración con otras herramientas. Por ejemplo, un flag --json podría volcar los resultados como JSON legible por máquina.
Integración con frameworks existentes: La lógica podría integrarse en un framework de escaneo más grande (por ejemplo, convirtiéndola en un módulo para OWASP ZAP o integrándola con Nuclei de ProjectDiscovery generando un informe compatible). Como mínimo, asegurarse de que la salida del script identifique claramente la vulnerabilidad y las URLs afectadas para que pueda usarse en informes.
Sesiones de navegador en paralelo: Si se apuntan aplicaciones muy grandes, considera lanzar múltiples contextos de navegador en paralelo para rastrear diferentes secciones de forma concurrente. Playwright puede manejar múltiples contextos (cada contexto está aislado, similar a perfiles de navegador separados). Esto podría acelerar el rastreo significativamente a costa de un mayor uso de recursos.
Degradación elegante: Si Playwright falla (digamos que el entorno carece de display o de una instalación adecuada), el script podría recurrir a un rastreo más simple basado en requests (que podría perder algunos enlaces pero es mejor que nada). Esto hace que la herramienta sea más robusta en varios entornos. De manera similar, si la concurrencia se establece demasiado alta y causa problemas, captura esos y sugiere al usuario reducir el número de hilos.
Siguiendo una estructura limpia y estas mejores prácticas, el script será más fácil de mantener y extender. Cada componente puede trabajarse de forma independiente – por ejemplo, mejorar la capacidad del rastreador para analizar navegación pesada en JavaScript, o actualizar el escáner con nuevas variaciones de payloads de encabezado si investigaciones futuras encuentran patrones de explotación adicionales.
En conclusión, este diseño describe un enfoque integral para detectar CVE-2025-29927 en aplicaciones web. Aprovecha un navegador headless para un rastreo profundo, multi-threading para un escaneo eficiente y prácticas de codificación robustas para la fiabilidad. Al comparar respuestas con y sin el encabezado especial, puede identificar de manera fiable endpoints vulnerables donde se está evadiendo el middleware de Next.js. El resultado es una herramienta de nivel profesional que ayuda a ingenieros de seguridad y desarrolladores a encontrar y abordar rápidamente esta vulnerabilidad crítica en sus aplicaciones.