
Comunicado público e análise técnica para CVE-2026-36590, uma vulnerabilidade de negação de serviço do NanoMQ v0.24.9.
Este repositório contém o aviso público e a análise técnica para CVE-2026-36590, uma vulnerabilidade de negação de serviço que afeta o EMQ NanoMQ v0.24.9.
Este repositório é público para fornecer uma referência técnica acessível para a revisão do CVE. A classificação final do CVE-2026-36590 será determinada pela Equipe CVE ou pelo CNA relevante.
A equipe do NanoMQ contestou este problema e o considera relacionado à configuração. Este repositório se concentra nas evidências técnicas, incluindo materiais de reprodução, resultados ASAN, logs de tempo de execução e análise de código-fonte.
/nanomq/nng/src/supplemental/mqtt/mqtt_qos_db_api.c/ nni_qos_db_setQuando o NanoMQ v0.24.9 é compilado sem suporte a SQLite enquanto está configurado como , o ponteiro do banco de dados QoS pode permanecer . Durante o tratamento de mensagens QoS MQTT, pode retornar cedo sem liberar uma referência de mensagem clonada. Mensagens repetidas MQTT PUBLISH com QoS > 0 podem causar consumo contínuo de memória, eventualmente levando à negação de serviço.
sqlite.enabletrueNULLnni_qos_db_setO problema depende de uma configuração de tempo de execução não padrão, mas os seguintes fatos mostram por que ainda requer revisão adicional:
O NanoMQ aceita a configuração de tempo de execução e continua em execução, em vez de relatar que o suporte a SQLite não está disponível na compilação atual.
Essa incompatibilidade de configuração pode acontecer na prática. Um usuário pode compilar o NanoMQ com o comando de compilação normal, mas depois copiar ou ativar a seção de persistência SQLite de um arquivo de configuração de exemplo.
O caminho de código afetado não valida completamente esse estado inconsistente. Quando a configuração de tempo de execução trata o SQLite como ativado, mas o ponteiro do banco de dados é NULL, nni_qos_db_set() retorna cedo sem liberar o objeto de mensagem passado.
Depois que essa configuração é aceita, o problema pode ser desencadeado por tráfego MQTT remoto. Mensagens PUBLISH repetidas com QoS > 0 podem entrar no caminho afetado e causar vazamentos de memória acumulados.
Testes ASAN com 1, 50 e 500 rodadas de reprodução mostram que os bytes vazados e as alocações aumentam com o número de tentativas de disparo. Isso sugere que o problema depende da quantidade de entrada, em vez de ser um pequeno vazamento único.
Os usuários devem restringir o acesso às instâncias do NanoMQ, evitar expor serviços MQTT a clientes não confiáveis e evitar ativar a persistência SQLite em compilações sem suporte a SQLite. O fornecedor deve adicionar validação de inicialização para a configuração SQLite e liberar a referência da mensagem no ramo db == NULL de nni_qos_db_set.
Docker/Docker_Reproduction_Guide.mdResumo Este repositório contém a Prova de Conceito (PoC) e a análise detalhada de uma vulnerabilidade de vazamento de memória encontrada no NanoMQ. O problema permite que atacantes remotos causem uma Negação de Serviço (DoS) exaurindo a memória do sistema por meio de mensagens MQTT QoS específicas.
Realizei uma análise aprofundada de um fenômeno de vazamento de memória no Nanomq. Por meio de instrumentação dinâmica e revisão de código, identifiquei um caminho crítico de vazamento de recursos acionado quando a configuração de tempo de execução ativa o SQLite (sqlite.enable=true), mas o binário é compilado sem suporte a SQLite (NNG_SUPP_SQLITE não definido).
Para manter este problema conciso, resumi as principais descobertas abaixo. Para a discriminação técnica completa (incluindo rastreamentos ASAN detalhados, diagramas de ciclo de vida e logs de instrumentação), consulte o arquivo Analysis_Report.md anexado.
Usando o AddressSanitizer, localizei a origem da memória vazada em nni_msg_alloc dentro de tcptran_pipe_recv_cb (nng/src/sp/transport/mqtt/broker_tcp.c). A memória foi alocada, mas nunca totalmente liberada.
Analisei o mecanismo de contagem de referências de nni_msg. Um ciclo de vida normal de mensagem QoS 1 deve envolver:
Como o GDB era impraticável para este cenário de alta concorrência, usei instrumentação dinâmica para rastrear o ciclo de vida de objetos de memória específicos (por exemplo, 0x60e00002ffa0).
A Descoberta: Os logs confirmaram uma incompatibilidade no ciclo de vida:
nmq_pipe_send_start_v4.Rastrear o fluxo de execução revelou por que a 3ª Liberação estava faltando:
-DNNG_SUPP_SQLITE, fazendo com que a lógica de inicialização em nano_sock_setdb fosse removida. No entanto, nanomq.conf tinha sqlite.enable = true. Isso fez com que o ponteiro db permanecesse NULL.nni_qos_db_set para armazenamento, a função verifica o ponteiro 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) {
// FALHA CRÍTICA:
// O retorno antecipado é acionado porque db é NULL.
// A função possui a propriedade de 'msg' (Ref++) mas falha ao liberá-la.
return;
}
// ...
}
A função retorna silenciosamente sem chamar nni_msg_free(msg). Isso resulta na perda irremediável do ponteiro msg, efetivamente órfão do bloco de memória.
nni_qos_db_set. Ela não lida com a propriedade do ponteiro msg ao executar um retorno antecipado devido a um estado de DB inválido.Recomendações:
nano_sock_setdb ou main) para garantir que o SQLite esteja funcionando corretamente. Se conf->sqlite.enable == true for detectado, mas a macro NNG_SUPP_SQLITE não estiver definida ou o SQLite estiver anormal, o sistema deve sair com um erro ou forçar a definição de enable para false e imprimir um aviso.nni_qos_db_set. Se db == NULL, nni_msg_free(msg) deve ser chamado para equilibrar a contagem de referências e evitar vazamentos de memória.