79% frente al 51%: los neobancos independientes quedan atrás en seguridad de aplicaciones. Leer el informe

Análisis a fondo

Análisis a fondo

MongoDB: un use-after-free en los replica sets permite un bloqueo no autenticado (CVE-2026-118383)

Solicitudes no autenticadas pueden tumbar un primario de replica set de MongoDB retrasado antes de 4.4.32, 5.0.35, 6.0.30, 7.0.48, 8.0.37, 8.3.16 y 9.0.7.

Resumen

  • Un use-after-free en la fase de recuperación de MongoDB permite a un cliente no autenticado plantar waiters que tumban un miembro del replica set cuando se convierte en primario. MongoDB publicó CVE-2026-118383 el 10 de noviembre de 2026.
  • Todos los miembros de un replica set de las ramas afectadas están expuestos, incluidos los miembros de shard y de servidor de configuración. Un mongod independiente no hace recuperación y no se ve afectado.
  • MongoDB la califica CVSS v4.0 9.5 Crítica. Datadog reprodujo un bloqueo; MongoDB dice que la ejecución de código puede ser posible en ciertas condiciones. No se reportan ataques, y no está en el catálogo KEV de la CISA al 13 de noviembre de 2026.
  • Actualice cada miembro a la versión corregida de su rama: 4.4.32, 5.0.35, 6.0.30, 7.0.48, 8.0.37, 8.3.16 o 9.0.7. Consulte «¿Cómo lo corrijo?» para Atlas y contenedores.

¿Estoy afectado?

Está expuesto cuando se cumplen todas estas condiciones:

  • El servidor es miembro de un replica set de MongoDB, incluido un miembro de shard o de servidor de configuración en un clúster fragmentado.
  • Su versión está en uno de los rangos afectados de la tabla.
  • Un atacante puede alcanzar el puerto MongoDB del miembro y enviar comandos ping con readConcern.afterOpTime. En las versiones vulnerables el servidor acepta esas solicitudes antes de comprobar las credenciales, así que activar la autenticación no bloquea la preparación.

Un mongod independiente no está expuesto. No tiene estado de recuperación de primario y no puede participar en la elección que necesita el bloqueo.

El bloqueo también necesita que el miembro se retrase y luego gane una elección. El atacante no puede provocar ninguna de las dos cosas, así que los waiters plantados son una trampa que se activa durante un mantenimiento real o una conmutación por error.

Rama Versiones afectadas Versión corregida
9.0 9.0.0 a 9.0.6 9.0.7
8.3 8.3.0 a 8.3.15 8.3.16
8.0 8.0.0 a 8.0.36 8.0.37
7.0 7.0.0 a 7.0.47 7.0.48
6.0 6.0.0 a 6.0.29 6.0.30
5.0 5.0.0 a 5.0.34 5.0.35
4.4 4.4.0 a 4.4.31 4.4.32

El aviso de MongoDB no incluye la rama 8.2, que llegó al final de su vida útil el 31 de julio de 2026 y no recibe correcciones. La prueba de concepto pública indica que 8.2.12 todavía mostraba el fallo en una prueba de laboratorio con un ajuste reservado a pruebas. Trate 8.2 como no compatible y migre a una rama corregida.

Compruebe la versión de cada miembro, no solo del primario:

# On each host, including members in containers and members that were offline
mongosh --quiet --eval 'db.version()'

# In a replica set, list every member and its state
mongosh --quiet --eval 'rs.status().members.map(m => m.name + " " + m.stateStr)'

# Docker
docker exec <container> mongosh --quiet --eval 'db.version()'

# Kubernetes, for each member pod
kubectl exec <pod> -- mongosh --quiet --eval 'db.version()'

rs.status() devuelve un error en un servidor independiente, lo que confirma que ese host no es miembro de un replica set. Para Atlas, abra la configuración de mantenimiento del clúster; MongoDB indica que los clústeres se actualizan automáticamente durante el mantenimiento.

¿Cómo lo corrijo?

Actualice cada miembro del replica set a la versión corregida de su rama. Una actualización parcial deja expuestos a los demás miembros, y una elección posterior puede trasladar el rol de primario a uno de ellos.

  1. Haga una copia de seguridad del despliegue y confirme la versión corregida de cada rama en la página de descargas del proveedor.
  2. Actualice un miembro cada vez y deje que se reincorpore antes de pasar al siguiente. En un replica set, haga primero que el primario ceda el rol, o actualice un secundario, para que el conjunto conserve un primario durante la operación.
  3. Después de cada actualización, ejecute db.version() en ese miembro y confirme la versión corregida. Compruebe también los miembros que estaban fuera de línea durante la ventana de mantenimiento.
  4. Para Docker, descargue la imagen corregida y recree el contenedor. Reemplazar la imagen sin recrear el contenedor deja el proceso antiguo en ejecución. La prueba de concepto pública usa mongodb/mongodb-community-server:8.0.37-ubi9 para su ejecución de control corregida.
  5. Para Atlas, aplique el mantenimiento pendiente. Si el proyecto aplaza el mantenimiento, MongoDB recomienda aplicar la actualización lo antes posible.

No hay ningún ajuste del servidor que desactive la ruta de recuperación vulnerable. Restringir el acceso de red al miembro, por ejemplo a los hosts de aplicación y a los demás miembros, reduce quién puede plantar los waiters. No elimina los waiters ya plantados y no corrige la carrera. Mantenga la restricción solo hasta que todos los miembros estén actualizados.

Las versiones corregidas cambian dos cosas. La devolución de llamada de recuperación ahora solo concluye un estado de recuperación vivo y actual, y solo los usuarios internos pueden enviar readConcern.afterOpTime. Los dos cambios son necesarios: el primero detiene la doble limpieza y el segundo impide que un cliente plante los waiters.

¿Cuál es la causa raíz?

La devolución de llamada de fin de recuperación del primario guardaba un puntero sin procesar a un estado que puede liberarse mientras la devolución de llamada se ejecuta. Una recuperación abortada vuelve a llamar a abort_inlock() sobre memoria liberada.

ReplicationCoordinatorImpl mantiene un CatchupState mientras un primario recién elegido aplica las operaciones que se perdió. El estado registra un waiter en _lastAppliedOpTimeWaiterList, un multimapa indexado por OpTime. Cuando el OpTime aplicado alcanza el objetivo, el waiter se completa y la devolución de llamada llama a abort_inlock() para salir del modo de recuperación. abort_inlock() restablece _catchupState, lo que destruye el objeto de estado.

La devolución de llamada vulnerable ignoraba el estado de finalización y no comprobaba que su estado siguiera siendo el actual. En MongoDB 8.0.34, en la línea 5267 de src/mongo/db/repl/replication_coordinator_impl.cpp, la devolución de llamada es:

auto targetOpTimeCB = [this](Status status) {
    // Double check the target time since stepdown may signal us too.
    const auto myLastApplied = _repl->_getMyLastAppliedOpTime_inlock();
    if (_targetOpTime <= myLastApplied) {
        ...
        abort_inlock(PrimaryCatchUpConclusionReason::kSucceeded);
    }
};

El estado se guardaba en un std::unique_ptr, así que el objeto no tenía una vida útil compartida. El encabezado declara en la línea 1972 std::unique_ptr<CatchupState> _catchupState;.

La prueba de concepto pública añade el paso que falta. Dos waiters readConcern.afterOpTime con términos OpTime elegidos crean un ciclo de orden en el multimapa, así que el waiter real de recuperación nunca es alcanzado por setValueIf_inlock(). El siguiente latido concluye que el miembro ya está al día y aborta la recuperación. Ese aborto restablece el waiter no completado, lo que ejecuta la devolución de llamada de forma síncrona. La devolución de llamada llama otra vez a abort_inlock(), y el marco exterior sigue leyendo el estado que la llamada interior liberó.

¿Cómo se explota?

  1. El atacante envía dos comandos ping no autenticados con readConcern.afterOpTime a un miembro alcanzable. Los términos OpTime elegidos ordenan mal los waiters en el multimapa, así que el waiter real de recuperación no puede completarse.
  2. Los waiters permanecen. No expiran y sobreviven hasta que el miembro se reinicia, hace rollback o deja el rol de primario.
  3. El miembro se retrasa después respecto a otro miembro y gana una elección. El atacante no puede provocar el retraso ni la elección, así que este paso depende de un mantenimiento real o de una caída.
  4. Cuando termina la recuperación, la limpieza se ejecuta dos veces. El nuevo primario lee y escribe memoria ya liberada, y se bloquea antes de aceptar escrituras.
  5. Datadog reprodujo el bloqueo en 8.0.34 con un fallo de segmentación en CatchupState::abort_inlock(). El laboratorio usó acceso de administrador para pausar un miembro y forzar la conmutación; solo las dos solicitudes ping vinieron de un cliente no autenticado.

El aviso de MongoDB indica que la falla también puede permitir la ejecución de código arbitrario en el proceso del servidor en ciertas condiciones. Datadog no logró ejecución de código en su laboratorio y no afirma que el bloqueo sea fiable en todos los despliegues.

¿Cómo se desarrolla el ataque?

Un cliente no autenticado planta dos waiters afterOpTime, una conmutación posterior convierte ese miembro en primario mientras está retrasado, y la limpieza de la recuperación se ejecuta dos veces sobre memoria liberada.

flowchart LR A["Dos pings no autenticados
con readConcern.afterOpTime"] --> B["Waiters guardados en el
multimapa lastAppliedOpTime"] B --> C["El orden de OpTime deja
sin completar el waiter real"] C --> D["El miembro se retrasa
y luego gana una elección"] D --> E["Termina la recuperación y
abort_inlock() restablece el waiter"] E --> F["La devolución de llamada
vuelve a abort_inlock() sobre el estado liberado"] F --> G["El nuevo primario se bloquea
antes de aceptar escrituras"]

¿Qué puede hacer un atacante?

Un atacante no autenticado puede tumbar un miembro del replica set en el momento en que se convierte en primario, lo que retira el primario del conjunto e interrumpe las escrituras hasta que otro miembro asume el rol.

El impacto depende del rol del miembro que se bloquea:

  • Un primario de un replica set normal que se bloquea provoca una elección y una breve interrupción de las escrituras. Otro miembro puede asumir el rol si está disponible.
  • Un primario de shard que se bloquea puede paralizar las operaciones que pasan por ese shard hasta que se elija un nuevo primario.
  • Un primario de servidor de configuración que se bloquea puede bloquear los cambios de metadatos del clúster hasta que el conjunto de configuración elija un nuevo primario.

Las condiciones forman parte del impacto:

  • El atacante necesita alcanzar el puerto MongoDB del miembro.
  • La solicitud no necesita cuenta ni interacción del usuario.
  • El miembro debe ganar después una elección mientras está retrasado respecto a otro miembro. El atacante no puede forzarlo, así que el bloqueo es una trampa diferida y no un ataque inmediato.
  • Un mongod independiente no se ve afectado.

MongoDB indica que la falla puede permitir la ejecución de código arbitrario en ciertas condiciones. Ninguna fuente demuestra esa ejecución, y la reproducción de Datadog terminó en un bloqueo. Trate la ejecución de código como una evaluación del proveedor, no como una capacidad demostrada.

¿Por qué funciona la corrección?

La corrección da una vida útil compartida al estado de recuperación y hace que la devolución de llamada compruebe el estado del waiter y si su estado sigue siendo el actual. La devolución de llamada ya no puede ejecutar abort_inlock() por segunda vez.

El diff entre MongoDB 8.0.34 y 8.0.37 cambia la propiedad y la devolución de llamada:

-    _catchupState = std::make_unique<CatchupState>(this);
+    _catchupState = std::make_shared<CatchupState>(this);
...
-    auto targetOpTimeCB = [this](Status status) {
+    auto targetOpTimeCB = [weakSelf = weak_from_this()](Status status) {
+        if (!status.isOK()) {
+            return;
+        }
+        auto self = weakSelf.lock();
+        if (!self || self->_repl->_catchupState != self) {
+            return;
+        }
         const auto myLastApplied = _repl->_getMyLastAppliedOpTime_inlock();
         if (_targetOpTime <= myLastApplied) {
             ...
-            abort_inlock(PrimaryCatchUpConclusionReason::kSucceeded);
+            self->abort_inlock(PrimaryCatchUpConclusionReason::kSucceeded);
         }
     };

El encabezado hace que CatchupState herede de std::enable_shared_from_this<CatchupState> y lo guarda en un std::shared_ptr. La devolución de llamada conserva un weak_ptr, así que solo puede concluir un estado que sigue vivo y sigue instalado en _catchupState. Un estado no OK significa que otra ruta ya concluyó la recuperación, y la devolución de llamada vuelve sin tocar el estado.

La segunda corrección, el commit 6111165c32, exige el privilegio de usuario interno para un readConcern.afterOpTime proporcionado por el cliente. Eso elimina la condición de plantado de waiters para los clientes ordinarios. Los dos commits forman la corrección de 8.0.37; el proveedor enumera las versiones corregidas correspondientes para cada rama afectada.

¿Cómo compruebo si estuve comprometido?

No se ha reportado explotación y no se han publicado indicadores de compromiso. Las comprobaciones buscan la firma del bloqueo y las solicitudes de los waiters en los registros del servidor.

  • Busque una entrada Slow query con el identificador de registro 51803 para un ping no autenticado que llevaba readConcern.afterOpTime y terminó con MaxTimeMSExpired.
  • Busque un miembro que entra en la recuperación de primario (identificador 21359) y luego sale dos veces (identificador 21363) en una misma transición.
  • Busque una señal fatal o una traza que nombre CatchupState::abort_inlock() después de esas dos salidas.
  • Revise registros anteriores del mismo miembro. Las dos solicitudes pueden llegar antes de la conmutación, y un miembro que se convierte en primario más tarde las conserva.

Una elección normal o una sola salida de recuperación no coinciden con este patrón. Que los campos de la solicitud aparezcan en el registro depende de la configuración de registro, y el campo attr.remote puede mostrar una dirección de bucle local en lugar de la dirección real del cliente. Un intento fallido puede no dejar rastro.

Cronología

  • 2026-10-30: MongoDB escribe las dos correcciones del hotfix 8.0.37, una para el estado de recuperación y otra para readConcern.afterOpTime.
  • 2026-11-10: MongoDB publica CVE-2026-118383 a las 20:23 UTC y publica las versiones corregidas. Los dos commits de corrección llegan a las 23:07 y 23:09 UTC.
  • 2026-11-11: Datadog Security Labs publica su análisis y reproducción, y se crea la prueba de concepto pública.
  • 2026-11-12: The Hacker News informa de la falla y de la declaración de MongoDB de que no tiene conocimiento de explotación.
  • 2026-11-13: el catálogo KEV de la CISA no incluye la CVE.

Del arreglo a la prueba de concepto pública en 12 días
Figura 1: el arreglo se escribió el 30 de octubre de 2026, la CVE y las versiones corregidas llegaron el 10 de noviembre, y el análisis público y la prueba de concepto el 11 de noviembre. Fuente: MongoDB, Datadog Security Labs y la prueba de concepto pública, verificado el 13 de noviembre de 2026

Preguntas frecuentes

¿Se explota CVE-2026-118383 en entornos reales?

No. MongoDB dice que encontró la falla internamente y que no tiene conocimiento de intentos de aprovecharla. La CVE no está en el catálogo KEV de la CISA al 13 de noviembre de 2026.

¿La autenticación detiene el ataque?

No en una versión afectada. Los dos comandos ping se aceptan antes de que el servidor compruebe las credenciales, por eso Datadog pudo plantar los waiters con un cliente no autenticado. La corrección de 8.0.37 rechaza un readConcern.afterOpTime proporcionado por un cliente que no sea un usuario interno.

¿Se puede tumbar un mongod independiente de esta forma?

No. Un servidor independiente no tiene replica set ni estado de recuperación de primario, así que la doble limpieza que provoca el use-after-free no puede ejecutarse. El aviso excluye las instancias independientes.

¿Puede el atacante forzar la conmutación?

No. El atacante puede plantar los waiters, pero el miembro debe retrasarse y ganar una elección por sí mismo. En el laboratorio de Datadog, los investigadores usaron acceso de administrador para pausar un miembro y desencadenar la elección; solo las dos solicitudes no estaban autenticadas.

¿Qué ocurre con un clúster de Atlas?

MongoDB dice que los clústeres de Atlas se actualizan automáticamente a versiones corregidas durante el mantenimiento del clúster. Si el proyecto aplaza el mantenimiento, MongoDB recomienda aplicar la actualización lo antes posible.

Uso la rama 8.2. ¿Está corregida?

El aviso no incluye 8.2, que llegó al final de su vida útil el 31 de julio de 2026. La prueba de concepto pública indica que 8.2.12 todavía mostraba el fallo con un ajuste reservado a pruebas. Migre el despliegue a una rama compatible y a una versión corregida.

Encuentre los miembros de MongoDB expuestos con Ostorlab

El descubrimiento de superficie de ataque de Ostorlab inventaría los hosts y servicios expuestos a Internet de su organización. Úselo para listar los puertos MongoDB que expone, de modo que sepa qué miembros actualizar o restringir primero. Es un inventario de hosts expuestos, no una detección de esta CVE.

Referencias

  • Registro CVE-2026-118383, CNA MongoDB, publicado el 2026-11-10, CVSS v4.0 9.5 y CVSS v3.1 9.0: https://www.cve.org/CVERecord?id=CVE-2026-118383 (verificado el 13 de noviembre de 2026)
  • MongoDB, «Important MongoDB security update available (Action Required) - November 10, 2026», la declaración del proveedor y las instrucciones de mantenimiento de Atlas: https://www.mongodb.com/community/forums/t/important-mongodb-security-update-available-action-required-november-10-2026/344843 (verificado el 13 de noviembre de 2026)
  • Datadog Security Labs, «CVE-2026-118383: Use-after-free vulnerability in MongoDB», 11 de noviembre de 2026, la reproducción, los registros del bloqueo y las condiciones de explotación: https://securitylabs.datadoghq.com/articles/cve-2026-118383-mongodb-server-use-after-free/ (verificado el 13 de noviembre de 2026)
  • Prueba de concepto y análisis públicos, el disparador de dos solicitudes, el ciclo de orden de los waiters y los commits de corrección: https://github.com/googlesky/CVE-2026-118383 (verificado el 13 de noviembre de 2026)
  • Commit de corrección 23dfc542199ab3b88d6a9420b6cd3f125a897620, «SERVER-138175 [v8.0.37-hotfix] Conclude primary catch-up only from a live, current catch-up state», fusionado el 2026-11-10: https://github.com/mongodb/mongo/commit/23dfc542199ab3b88d6a9420b6cd3f125a897620 (verificado el 13 de noviembre de 2026)
  • Commit de corrección 6111165c32bfc74f197e2dfeb9480d0b52154d68, «SERVER-138119 [v8.0.37-hotfix] Allow only internal users to use readConcern.afterOpTime», fusionado el 2026-11-10: https://github.com/mongodb/mongo/commit/6111165c32bfc74f197e2dfeb9480d0b52154d68 (verificado el 13 de noviembre de 2026)
  • Código fuente de MongoDB 8.0.34, la devolución de llamada vulnerable en la línea 5267 de src/mongo/db/repl/replication_coordinator_impl.cpp y el unique_ptr en la línea 1972 del encabezado: https://github.com/mongodb/mongo/blob/r8.0.34/src/mongo/db/repl/replication_coordinator_impl.cpp (verificado el 13 de noviembre de 2026)
  • Código fuente de MongoDB 8.0.37, la devolución de llamada corregida y la propiedad compartida: https://github.com/mongodb/mongo/blob/r8.0.37/src/mongo/db/repl/replication_coordinator_impl.cpp (verificado el 13 de noviembre de 2026)
  • Página de descargas de MongoDB, las compilaciones corregidas de Community Server: https://www.mongodb.com/try/download/community (verificado el 13 de noviembre de 2026)
  • Calendario de ciclo de vida de MongoDB, el final de vida de 8.2 el 31 de julio de 2026: https://www.mongodb.com/legal/support-policy/lifecycles (verificado el 13 de noviembre de 2026)
  • Docker Hub, la imagen oficial mongo y su número de descargas: https://hub.docker.com/_/mongo (verificado el 13 de noviembre de 2026)
  • Flujo JSON del catálogo de vulnerabilidades explotadas de la CISA, comprobado para CVE-2026-118383 el 13 de noviembre de 2026 (no incluida): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (verificado el 13 de noviembre de 2026)
  • The Hacker News, «Critical Pre-Auth MongoDB Flaw (CVE-2026-118383) Can Crash Lagging Servers on Failover», 12 de noviembre de 2026, la reproducción de Datadog y la declaración del proveedor: https://thehackernews.com/2026/11/critical-pre-auth-mongodb-flaw-cve-2026.html (verificado el 13 de noviembre de 2026)