
Aviso público y análisis técnico para CVE-2026-36590, una vulnerabilidad de denegación de servicio en NanoMQ v0.24.9.
Este repositorio contiene el aviso público y el análisis técnico del CVE-2026-36590, una vulnerabilidad de denegación de servicio que afecta a EMQ NanoMQ v0.24.9.
Este repositorio es público para proporcionar una referencia técnica accesible para la revisión del CVE. La clasificación final del CVE-2026-36590 será determinada por el Equipo CVE o la CNA correspondiente.
El equipo de NanoMQ ha cuestionado este problema y lo considera relacionado con la configuración. Este repositorio se centra en la evidencia técnica, incluidos los materiales de reproducción, los resultados de ASAN, los registros de ejecución y el análisis del código fuente.
/nanomq/nng/src/supplemental/mqtt/mqtt_qos_db_api.c/ nni_qos_db_setCuando NanoMQ v0.24.9 se compila sin soporte de SQLite mientras sqlite.enable está configurado como true, el puntero de la base de datos QoS puede permanecer NULL. Durante el manejo de mensajes MQTT QoS, nni_qos_db_set puede retornar de forma anticipada sin liberar una referencia de mensaje clonado. Los mensajes MQTT PUBLISH repetidos con QoS > 0 pueden causar un consumo continuo de memoria, lo que eventualmente conduce a una denegación de servicio.
El problema depende de una configuración de ejecución no predeterminada, pero los siguientes hechos muestran por qué aún requiere una revisión adicional:
NanoMQ acepta la configuración de ejecución y continúa funcionando, en lugar de informar que el soporte de SQLite no está disponible en la compilación actual.
Este desajuste de configuración puede ocurrir en la práctica. Un usuario puede compilar NanoMQ con el comando de compilación normal, pero luego copiar o habilitar la sección de persistencia de SQLite de un archivo de configuración de ejemplo.
La ruta de código afectada no valida completamente este estado inconsistente. Cuando la configuración de ejecución considera SQLite como habilitado pero el puntero de la base de datos es NULL, nni_qos_db_set() retorna de forma anticipada sin liberar el objeto de mensaje recibido.
Después de que se acepta esta configuración, el problema puede ser activado por tráfico MQTT remoto. Los mensajes PUBLISH repetidos con QoS > 0 pueden entrar en la ruta afectada y causar fugas de memoria acumuladas.
Las pruebas ASAN con 1, 50 y 500 rondas de reproducción muestran que los bytes y asignaciones filtrados aumentan con el número de intentos de activación. Esto sugiere que el problema depende de la cantidad de entradas, en lugar de ser una fuga pequeña única.
Los usuarios deben restringir el acceso a las instancias de NanoMQ, evitar exponer los servicios MQTT a clientes no confiables y evitar habilitar la persistencia de SQLite en compilaciones sin soporte de SQLite. El proveedor debe agregar una validación de inicio para la configuración de SQLite y liberar la referencia del mensaje en la rama db == NULL de nni_qos_db_set.
Docker/Docker_Reproduction_Guide.mdResumen Este repositorio contiene la Prueba de Concepto (PoC) y el análisis detallado de una vulnerabilidad de fuga de memoria encontrada en NanoMQ. El problema permite a atacantes remotos causar una Denegación de Servicio (DoS) al agotar la memoria del sistema mediante mensajes MQTT QoS específicos.
He llevado a cabo un análisis en profundidad de un fenómeno de fuga de memoria en Nanomq. Mediante instrumentación dinámica y revisión de código, identifiqué una ruta crítica de fuga de recursos que se activa cuando la configuración de ejecución habilita SQLite (sqlite.enable=true) pero el binario se compila sin soporte de SQLite (NNG_SUPP_SQLITE no definido).
Para mantener este problema conciso, he resumido los hallazgos clave a continuación. Para el desglose técnico completo (incluidas trazas ASAN detalladas, diagramas de ciclo de vida y registros de instrumentación), consulte el Analysis_Report.md adjunto.
Usando AddressSanitizer, identifiqué el origen de la memoria filtrada en nni_msg_alloc dentro de tcptran_pipe_recv_cb (nng/src/sp/transport/mqtt/broker_tcp.c). La memoria se asignó pero nunca se liberó por completo.
Analicé el mecanismo de conteo de referencias de nni_msg. Un ciclo de vida normal de un mensaje QoS 1 debería incluir:
Dado que GDB no era práctico para este escenario de alta concurrencia, utilicé instrumentación dinámica para rastrear el ciclo de vida de objetos de memoria específicos (p. ej., 0x60e00002ffa0).
El Hallazgo: Los registros confirmaron un desajuste en el ciclo de vida:
nmq_pipe_send_start_v4.El rastreo del flujo de ejecución reveló por qué faltaba la tercera liberación:
-DNNG_SUPP_SQLITE, lo que provocó que la lógica de inicialización en nano_sock_setdb se omitiera. Sin embargo, nanomq.conf tenía sqlite.enable = true. Esto provocó que el puntero db permaneciera NULL.nni_qos_db_set para su almacenamiento, la función verifica el puntero NULL:// File: /nanomq/nng/src/supplemental/mqtt/mqtt_qos_db_api.c
void nni_qos_db_set(..., void *db, ..., nng_msg *msg) {
if (db == NULL) {
// CRITICAL FLAW:
// Early return triggers because db is NULL.
// The function holds ownership of 'msg' (Ref++) but fails to release it.
return;
}
// ...
}
La función retorna silenciosamente sin llamar a nni_msg_free(msg). Esto hace que el puntero msg se pierda irremediablemente, dejando efectivamente huérfano el bloque de memoria.
nni_qos_db_set. No gestiona la propiedad del puntero msg al ejecutar un retorno anticipado debido a un estado de base de datos no válido.Recomendaciones:
nano_sock_setdb o main) para asegurar que SQLite se esté ejecutando correctamente. Si se detecta conf->sqlite.enable == true pero la macro NNG_SUPP_SQLITE no está definida o SQLite es anómalo, el sistema debe salir con un error o forzar el establecimiento de enable en false y mostrar una advertencia.nni_qos_db_set. Si db == NULL, se debe llamar a nni_msg_free(msg) para equilibrar el conteo de referencias y evitar fugas de memoria.