Neutron, nuestro motor de IA, obtuvo un 97.1% en el benchmark CyberGym de UC Berkeley. Más información

Análisis a fondo

Análisis a fondo

Elusión de inyección NoSQL en Mongoose, corregida en 9.11.4 (CVE-2026-125120)

Mongoose aplica mal sanitizeFilter: filtros controlados por el atacante llegan a countDocuments y a conexiones; corregido en 9.11.4.

Resumen

  • La defensa sanitizeFilter de Mongoose no se aplica en countDocuments, se ignora en una conexión, y tres rutas copian propiedades heredadas. Los filtros controlados por el atacante pueden eludir la autorización, alterar recuentos o tumbar consultas.
  • Mongoose anterior a 6.13.12, 7.8.13, 8.24.7 y 9.11.4 está afectado en las cuatro líneas compatibles. El fallo de countDocuments es CVE-2026-125120, con CVSS v3.1 9.8 Crítica.
  • No se conoce explotación en la naturaleza. Los avisos incluyen código de prueba de concepto, y ninguno de los fallos está en el catálogo Known Exploited Vulnerabilities de CISA a 11 de noviembre de 2026.
  • Actualice a 6.13.12, 7.8.13, 8.24.7 o 9.11.4. Si depende de sanitizeFilter, añada el middleware de countDocuments del aviso hasta actualizar todos los servicios. Véase «¿Cómo lo corrijo?».

¿Estoy afectado?

Todas estas condiciones deben cumplirse:

  • Su aplicación usa Mongoose y pasa valores controlados por el atacante a filtros, actualizaciones o construcción de documentos.
  • La aplicación depende de sanitizeFilter para protegerse. Puede ser un mongoose.set('sanitizeFilter', true) global, una opción de conexión o una opción por consulta.
  • Para el fallo crítico, la aplicación llama a countDocuments con un filtro que contiene valores controlados por el atacante.

Los otros dos avisos amplían las condiciones. El fallo de conexión afecta a las aplicaciones que configuran sanitizeFilter en una conexión creada con mongoose.createConnection(uri, { sanitizeFilter: true }). El fallo de propiedades heredadas afecta a las aplicaciones que cargan documentos o establecen rutas con puntos usando nombres de campo controlados por el atacante, incluso sin sanitizeFilter.

Mongoose es una biblioteca de MongoDB ampliamente usada para Node.js. El registro npm registró 8,868,838 descargas del paquete mongoose en la semana que terminó el 9 de noviembre de 2026, y el repositorio de GitHub tiene 27,449 estrellas.

Rama Versiones afectadas Versión corregida
9.x 9.0.0 a 9.11.3 9.11.4, publicada el 2026-10-23
8.x 8.0.0 a 8.24.6 8.24.7, publicada el 2026-10-23
7.x 7.0.0 a 7.8.12 7.8.13
6.x 6.0.0 a 6.13.11 6.13.12

La tabla usa el umbral de corrección más alto de los tres avisos. El fallo de countDocuments por sí solo se corrigió en 9.9.2, 8.24.3, 7.8.12 y 6.13.11 el 10 de agosto de 2026. Las correcciones de conexión y propiedades heredadas llegaron después, en 9.11.4, 8.24.7, 7.8.13 y 6.13.12. Instalar solo las versiones de agosto deja abiertos los dos fallos High.

Compruebe la versión instalada:

npm ls mongoose
grep -A2 '"node_modules/mongoose"' package-lock.json
yarn why mongoose
pnpm list mongoose

Después busque dónde se configura la defensa:

grep -R "sanitizeFilter" --include='*.js' --include='*.ts' .

Una conexión creada con { sanitizeFilter: true } está expuesta a CVE-2026-125119 aunque connection.get('sanitizeFilter') devuelva true.

¿Cómo lo corrijo?

Actualice a la versión corregida de su línea principal y vuelva a desplegar cada servicio que use Mongoose.

  1. En Mongoose 9.x, instale 9.11.4 o posterior: npm install mongoose@9.11.4
  2. En Mongoose 8.x, instale 8.24.7 o posterior: npm install mongoose@8.24.7
  3. En Mongoose 7.x, instale 7.8.13 o posterior: npm install mongoose@7.8.13
  4. En Mongoose 6.x, instale 6.13.12 o posterior: npm install mongoose@6.13.12
  5. Reconstruya y vuelva a desplegar cada servicio, y ejecute sus pruebas de autenticación y autorización basadas en recuentos con la nueva versión.

Si no puede actualizar de inmediato, el aviso de countDocuments publica un middleware de solución provisional. Aplica sanitizeFilter antes de que se ejecute el recuento:

mongoose.plugin(function() {
  schema.pre('countDocuments', function() {
    // This will always apply `sanitizeFilter` on `countDocuments`
    mongoose.sanitizeFilter(this.getFilter());
  });
});

Para el fallo de conexión, no dependa de createConnection(uri, { sanitizeFilter: true }). Configure la opción globalmente con mongoose.set('sanitizeFilter', true) o por consulta con .sanitizeFilter(). Esa solución provisional no corrige el fallo de propiedades heredadas almacenadas; solo la actualización lo hace. Valide los nombres de campo que acepta su aplicación si no puede actualizar, y rechace rutas como toString, valueOf, constructor y $__schema.

¿Cuál es la causa raíz?

El sanitizeFilter de Mongoose envuelve los objetos operadores de los valores de filtro en $eq, para que un valor como { $ne: null } se compare literalmente en lugar de interpretarse como un operador de MongoDB. La defensa solo es eficaz si cada ruta de consulta la aplica. Estos tres avisos son tres huecos en esa promesa.

El primer hueco es countDocuments. En 8.24.2, _countDocuments convierte las condiciones directamente en lugar de pasar por _castConditions, la función que aplica sanitizeFilter:

Query.prototype._countDocuments = async function _countDocuments() {
  this._applyTranslateAliases();

  try {
    this.cast(this.model);
  } catch (err) {
    this.error(err);
  }

_castConditions comprueba las opciones configuradas y llama a sanitizeFilter(this._conditions), pero countDocuments nunca llega a ella. La corrección hace que el método llame a _castConditions().

El segundo hueco es la opción de conexión. NativeConnection#createClient() mueve sanitizeFilter de las opciones de conexión al objeto config de la conexión y lo elimina de options:

    if ('sanitizeFilter' in options) {
      this.config.sanitizeFilter = options.sanitizeFilter;
      delete options.sanitizeFilter;
    }

Query.prototype._castConditions en 8.24.6 lee this.model?.db.options?.sanitizeFilter, luego this.model?.base.options?.sanitizeFilter, luego la opción por consulta. Nunca lee this.model.db.config.sanitizeFilter, así que la opción de conexión se ignora en silencio aunque connection.get('sanitizeFilter') devuelva true.

El tercer hueco son las propiedades heredadas. Mongoose bloquea __proto__, constructor y prototype, pero su código de hidratación y de establecimiento de rutas no comprueba si una propiedad pertenece al objeto. En init(), un campo almacenado llamado toString resuelve la propiedad heredada Object.prototype.toString, que es verdadera, así que Mongoose recurre dentro de la función y copia los subcampos almacenados sobre ella:

      // assume nested object
      if (!doc[i]) {
        doc[i] = {};
        if (!strict && !(i in docSchema.tree) && !(i in docSchema.methods) && !(i in docSchema.virtuals)) {
          self[i] = doc[i];
        } else if (opts?.virtuals && (i in docSchema.virtuals)) {
          self[i] = doc[i];
        }
      }
      init(self, value, doc[i], opts, path + '.');

La misma comprobación de propiedad propia falta en $__set, que maneja rutas con puntos, y en utils.mergeClone, que fusiona documentos de actualización. La corrección añade comprobaciones Object.hasOwn antes de escribir.

¿Cómo se explota?

  1. Para CVE-2026-125120, la aplicación llama a Model.countDocuments({ username: req.body.username, password: req.body.password }) con sanitizeFilter activado. El atacante envía {"password": {"$ne": null}}.
  2. Como countDocuments omitió _castConditions, el objeto operador llega a MongoDB como operador. El recuento devuelve la cuenta coincidente aunque la contraseña sea incorrecta.
  3. Una comprobación de autenticación o de cuota que trata un recuento distinto de cero como éxito concede el acceso o devuelve datos que el atacante no debería ver.
  4. Para CVE-2026-125119, la aplicación crea una conexión con sanitizeFilter: true y pasa {"password": {"$ne": null}} a findOne. La opción está en Connection.config, no en Connection.options, así que el filtro no se limpia y la consulta devuelve la primera cuenta.
  5. Para CVE-2026-125117, el atacante almacena o envía un campo llamado toString con un valor como {"call": 1}. La hidratación copia el valor sobre la función compartida, y todas las consultas posteriores del proceso lanzan TypeError: _toString.call is not a function.
  6. El mismo hueco puede alcanzar el esquema compartido de un subdocumento, por ejemplo estableciendo profile.$__schema.paths.email.validators en un array vacío, lo que elimina el validador required para todos los documentos del proceso.

Los avisos incluyen código de prueba de concepto para los fallos de conexión y de propiedades heredadas. El aviso crítico incluye una prueba que muestra el operador envuelto en $eq después de la corrección. No se informa de explotación pública en la naturaleza.

¿Cómo se desarrolla el ataque?

Un objeto operador controlado por el atacante llega a una ruta de consulta que omitió el saneamiento, así que MongoDB lo interpreta como un operador y devuelve datos o cambia un estado que la aplicación no pretendía.

flowchart LR A["El atacante envía un valor de filtro
como { $ne: null }"] --> B["La aplicación lo pasa a
countDocuments o una conexión"] B --> C["sanitizeFilter se omite
o se guarda en la opción equivocada"] C --> D["MongoDB trata el valor
como un operador"] D --> E["La consulta devuelve un registro
o un recuento en el que la app confía"] E --> F["Elusión de autenticación
o exposición de datos"]

¿Qué puede hacer un atacante?

El impacto depende de la ruta. El fallo de countDocuments permite al atacante cambiar el resultado de un recuento que la aplicación usa para autorización, paginación, cuotas o lógica de negocio. El fallo de conexión permite eludir una comprobación de contraseña y leer un registro de cuenta. El fallo de propiedades heredadas permite sobrescribir funciones integradas compartidas y provocar una denegación de servicio, o alterar validadores de esquema compartidos.

Las condiciones son:

  • La aplicación pasa objetos controlados por el atacante a filtros, actualizaciones o construcción de documentos de Mongoose.
  • Para el fallo crítico, la aplicación usa countDocuments y depende de sanitizeFilter.
  • Para el fallo de conexión, la aplicación configura sanitizeFilter en createConnection en lugar de globalmente o por consulta.
  • Para el fallo de propiedades heredadas, la aplicación acepta nombres de campo o rutas con puntos controlados por el atacante, o almacena documentos cuyos nombres de campo controla el atacante.

Los avisos describen divulgación de datos y denegación de servicio. No describen ejecución remota de código, y ninguna fuente demuestra ejecución de código por estas rutas.

¿Por qué funciona la corrección?

Las correcciones cierran cada hueco en el momento en que se prepara el filtro. Para countDocuments, el método ahora llama a _castConditions, así que sanitizeFilter se ejecuta antes de que la consulta llegue a MongoDB:

 Query.prototype._countDocuments = async function _countDocuments() {
   this._applyTranslateAliases();
-
-  try {
-    this.cast(this.model);
-  } catch (err) {
-    this.error(err);
-  }
+  this._castConditions();

   if (this.error()) {
     throw this.error();

Para la opción de conexión, _castConditions ahora lee primero el config de la conexión:

 Query.prototype._castConditions = function() {
-  let sanitizeFilterOpt = undefined;
-  if (this.model?.db.options?.sanitizeFilter != null) {
-    sanitizeFilterOpt = this.model.db.options.sanitizeFilter;
-  } else if (this.model?.base.options?.sanitizeFilter != null) {
-    sanitizeFilterOpt = this.model.base.options.sanitizeFilter;
-  } else {
-    sanitizeFilterOpt = this._mongooseOptions.sanitizeFilter;
-  }
+  const sanitizeFilterOpt = this.model?.db.config?.sanitizeFilter ??
+    this.model?.db.options?.sanitizeFilter ??
+    this.model?.base.options.sanitizeFilter ??
+    this._mongooseOptions.sanitizeFilter;

Para las propiedades heredadas, el código de documentos comprueba Object.hasOwn antes de escribir:

       // assume nested object
+      // Skip inherited properties like `Object.prototype.toString`
+      // to avoid setting properties on shared objects
+      if (!Object.hasOwn(doc, i) && i in doc) {
+        continue;
+      }
       if (!doc[i]) {
         doc[i] = {};

La misma comprobación llega al setter de rutas con puntos y al ayudante de fusión. Un campo toString almacenado ahora se ignora en lugar de copiarse sobre la función compartida, y una ruta con puntos que nombra una propiedad heredada se convierte en una operación sin efecto.

Las correcciones son completas para las tres rutas de código que describen los avisos. Las aplicaciones deben seguir evitando pasar objetos de petición sin procesar a filtros donde el esquema espera un escalar; sanitizeFilter reduce el riesgo pero no valida la semántica de la aplicación.

¿Cómo compruebo si estuve comprometido?

Los avisos no publican indicadores de compromiso y no se conoce explotación en la naturaleza. La evidencia sería a nivel de aplicación, no una firma de archivo o de red conocida.

Busque consultas que transporten objetos operadores donde su código espera un escalar. Si su aplicación registra los filtros de consulta, busque $ne, $gt, $where o $expr en valores derivados de peticiones:

grep -E '\$(ne|gt|gte|lt|lte|where|expr)' /var/log/your-app.log

Una coincidencia es una pista, no una prueba: el código legítimo puede usar esos operadores. Correlaciónela con eventos de autenticación o autorización basados en recuentos de la misma dirección de origen.

Busque el síntoma de denegación de servicio del fallo de propiedades heredadas en el registro de la aplicación:

grep -E '_toString\.call is not a function|_toString\.call is not' /var/log/your-app.log

Un proceso que empieza a lanzar ese error en cada consulta después de cargar o actualizar un documento es coherente con el fallo. Reiniciar el proceso borra el síntoma, pero el documento almacenado permanece, así que encuentre y limpie el registro.

Compruebe si las colecciones contienen documentos con nombres de campo que oculten funciones integradas. Esta consulta devuelve una pista, no un veredicto, porque un esquema legítimo puede usar esos nombres:

db.getCollectionNames().forEach(function(name) {
  const hit = db[name].findOne({ $or: [{ toString: { $exists: true } }, { valueOf: { $exists: true } }] });
  if (hit) { print(name); }
});

Si encuentra un documento escrito por un atacante, elimínelo, rote cualquier credencial que el proceso de la aplicación pudiera leer y revise los registros que devolvieron las consultas afectadas.

Cronología

  • 2026-08-09: se confirma la corrección de countDocuments.
  • 2026-08-10: Mongoose 6.13.11, 7.8.12, 8.24.3 y 9.9.2 incluyen la corrección de countDocuments.
  • 2026-10-22: se confirman las correcciones de conexión y propiedades heredadas.
  • 2026-10-23: Mongoose 8.24.7 y 9.11.4 incluyen el segundo conjunto de correcciones.
  • 2026-11-09: se publican los registros CVE-2026-125120, CVE-2026-125119 y CVE-2026-125117.
  • 2026-11-10: se publican los avisos de GitHub.

Cronología de las correcciones de agosto y octubre y los avisos de noviembre
Figura 1: las correcciones se publicaron en agosto y octubre, mientras que los registros CVE y los avisos de GitHub aparecieron en noviembre. Fuente: versiones y avisos de Mongoose, consultados el 11 de noviembre de 2026

Preguntas frecuentes

¿Afecta a todas las aplicaciones Mongoose?

No. El fallo crítico requiere que la aplicación llame a countDocuments con valores de filtro controlados por el atacante mientras depende de sanitizeFilter. El fallo de conexión requiere que la opción se configure en createConnection. El fallo de propiedades heredadas es más amplio: cualquier aplicación que cargue documentos o establezca rutas con puntos usando nombres de campo controlados por el atacante puede alcanzarlo. Actualizar es la opción segura para los tres.

¿Hay una prueba de concepto pública?

Sí para los fallos de conexión y de propiedades heredadas. Ambos avisos incluyen código que demuestra la elusión, y el bloque SSVC de CISA para CVE-2026-125117 registra Exploitation: poc. El aviso de countDocuments incluye una prueba de regresión en lugar de un exploit independiente.

¿Se explota en la naturaleza?

No se conoce explotación en la naturaleza a 11 de noviembre de 2026, y ninguno de los tres CVE está en el catálogo Known Exploited Vulnerabilities de CISA. El código público de prueba de concepto significa que la ventana entre la divulgación y los intentos es corta, así que actualice según el calendario normal sin esperar un informe.

¿Cuál es la versión segura mínima?

Use 6.13.12, 7.8.13, 8.24.7 o 9.11.4. Las versiones de agosto solo corrigen el fallo crítico de countDocuments; las de octubre añaden las correcciones de conexión y propiedades heredadas. Instalar 8.24.3 o 9.9.2 deja abiertos dos fallos High.

¿Por qué aparecieron los avisos meses después de las correcciones?

Las correcciones se publicaron en agosto y octubre de 2026, pero los avisos de GitHub y los registros CVE se publicaron el 9 y 10 de noviembre de 2026. Esa brecha es habitual en avisos de repositorio revisados más tarde. Las fechas anteriores provienen de las etiquetas de versión y de los registros CVE, y el artículo usa las fechas de corrección, no las de los avisos, para describir el historial.

¿Debo actualizar si no uso sanitizeFilter?

Sí, porque el fallo de propiedades heredadas no depende de sanitizeFilter. Depende de nombres de campo controlados por el atacante que llegan a la hidratación de documentos, al setter de rutas con puntos o al código de fusión. Si su esquema acepta claves arbitrarias, el fallo se aplica incluso cuando la defensa de filtro está desactivada.

Encuentre versiones vulnerables de Mongoose con Ostorlab

El análisis de composición de software y el SBOM de Ostorlab inventaría las dependencias de código abierto de sus repositorios e imágenes de contenedores, para ver qué servicios siguen resolviendo mongoose a una versión afectada y seguir la actualización a 6.13.12, 7.8.13, 8.24.7 o 9.11.4. Ejecútelo en los repositorios que construyen sus servicios Node.js y use los resultados para priorizar los que pasan datos de petición a filtros de consulta.

Referencias

  • Aviso de Mongoose GHSA-pcrw-h46h-c93h (CVE-2026-125120): https://github.com/Automattic/mongoose/security/advisories/GHSA-pcrw-h46h-c93h (consultado el 11 de noviembre de 2026)
  • Aviso de Mongoose GHSA-gg4r-jv3p-qx3c (CVE-2026-125119): https://github.com/Automattic/mongoose/security/advisories/GHSA-gg4r-jv3p-qx3c (consultado el 11 de noviembre de 2026)
  • Aviso de Mongoose GHSA-8332-j7wj-wjw4 (CVE-2026-125117): https://github.com/Automattic/mongoose/security/advisories/GHSA-8332-j7wj-wjw4 (consultado el 11 de noviembre de 2026)
  • Commit de corrección de countDocuments (7e912af3): https://github.com/Automattic/mongoose/commit/7e912af3fac52cb0eea4cd71e24d65318916831a (consultado el 11 de noviembre de 2026)
  • Commit de corrección de la opción de conexión (4bb65947): https://github.com/Automattic/mongoose/commit/4bb659478be2f30655a7bdd78bc251bdf111cd29 (consultado el 11 de noviembre de 2026)
  • Commit de corrección de propiedades heredadas (18b5513d): https://github.com/Automattic/mongoose/commit/18b5513dd463731a2caf0d3395ef1d9eb0735f50 (consultado el 11 de noviembre de 2026)
  • Versión Mongoose 9.11.4: https://github.com/Automattic/mongoose/releases/tag/9.11.4 (consultado el 11 de noviembre de 2026)
  • Versión Mongoose 8.24.7: https://github.com/Automattic/mongoose/releases/tag/8.24.7 (consultado el 11 de noviembre de 2026)
  • Descargas npm de mongoose: https://npm-stat.com/charts.html?package=mongoose&from=2026-11-03&to=2026-11-09 (consultado el 11 de noviembre de 2026)
  • Registro CVE-2026-125120: https://www.cve.org/CVERecord?id=CVE-2026-125120 (consultado el 11 de noviembre de 2026)
  • Registro CVE-2026-125117: https://www.cve.org/CVERecord?id=CVE-2026-125117 (consultado el 11 de noviembre de 2026)