
Öffentlicher Advisory und technische Analyse zu CVE-2026-36590, einer Denial-of-Service-Sicherheitslücke in NanoMQ v0.24.9.
Dieses Repository enthält das öffentliche Advisory und die technische Analyse für CVE-2026-36590, eine Denial-of-Service-Schwachstelle, die EMQ NanoMQ v0.24.9 betrifft.
Dieses Repository ist öffentlich, um eine zugängliche technische Referenz für die CVE-Begutachtung bereitzustellen. Die endgültige Einstufung von CVE-2026-36590 erfolgt durch das CVE-Team oder die zuständige CNA.
Das NanoMQ-Team hat dieses Problem bestritten und betrachtet es als konfigurationsbedingt. Dieses Repository konzentriert sich auf die technischen Belege, einschließlich Reproduktionsmaterialien, ASAN-Ergebnissen, Laufzeitprotokollen und Quellcode-Analyse.
/nanomq/nng/src/supplemental/mqtt/mqtt_qos_db_api.c/ nni_qos_db_setWenn NanoMQ v0.24.9 ohne SQLite-Unterstützung gebaut wird, während sqlite.enable als true konfiguriert ist, kann der QoS-Datenbankzeiger auf NULL verbleiben. Während der Verarbeitung von MQTT-QoS-Nachrichten kann nni_qos_db_set frühzeitig zurückkehren, ohne eine geklonte Nachrichtenreferenz freizugeben. Wiederholte MQTT-PUBLISH-Nachrichten mit QoS > 0 können einen kontinuierlichen Speicherverbrauch verursachen, was letztlich zu einem Denial of Service führt.
Das Problem hängt von einer nicht standardmäßigen Laufzeitkonfiguration ab, doch die folgenden Fakten zeigen, warum es weiterhin überprüft werden muss:
NanoMQ akzeptiert die Laufzeitkonfiguration und läuft weiter, anstatt zu melden, dass SQLite-Unterstützung im aktuellen Build nicht verfügbar ist.
Diese Konfigurationsinkonsistenz kann in der Praxis auftreten. Ein Benutzer kann NanoMQ mit dem normalen Build-Befehl kompilieren, später jedoch den Abschnitt zur SQLite-Persistenz aus einer Beispielkonfigurationsdatei kopieren oder aktivieren.
Der betroffene Codepfad validiert diesen inkonsistenten Zustand nicht vollständig. Wenn die Laufzeitkonfiguration SQLite als aktiviert behandelt, der Datenbankzeiger jedoch NULL ist, kehrt nni_qos_db_set() frühzeitig zurück, ohne das übergebene Nachrichtenobjekt freizugeben.
Nachdem diese Konfiguration akzeptiert wurde, kann das Problem durch entfernten MQTT-Verkehr ausgelöst werden. Wiederholte PUBLISH-Nachrichten mit QoS > 0 können in den betroffenen Pfad gelangen und sich anhäufende Speicherlecks verursachen.
ASAN-Tests mit 1, 50 und 500 Reproduktionsdurchläufen zeigen, dass die ausgeleakten Bytes und Speicherbelegungen mit der Anzahl der Auslöseversuche zunehmen. Dies deutet darauf hin, dass das Problem von der Anzahl der Eingaben abhängt und nicht ein einmaliges kleines Leck ist.
Benutzer sollten den Zugriff auf NanoMQ-Instanzen einschränken, MQTT-Dienste nicht für nicht vertrauenswürdige Clients freigeben und die Aktivierung der SQLite-Persistenz in Builds ohne SQLite-Unterstützung vermeiden. Der Hersteller sollte eine Startvalidierung für die SQLite-Konfiguration hinzufügen und die Nachrichtenreferenz im db == NULL-Zweig von nni_qos_db_set freigeben.
Docker/Docker_Reproduction_Guide.mdZusammenfassung Dieses Repository enthält den Proof of Concept (PoC) und die detaillierte Analyse einer in NanoMQ gefundenen Speicherleck-Schwachstelle. Das Problem ermöglicht es entfernten Angreifern, einen Denial of Service (DoS) zu verursachen, indem sie den Systemspeicher über bestimmte QoS-MQTT-Nachrichten erschöpfen.
Ich habe eine eingehende Analyse eines Speicherleck-Phänomens in Nanomq durchgeführt. Durch dynamische Instrumentierung und Code-Review habe ich einen kritischen Ressourcenleck-Pfad identifiziert, der ausgelöst wird, wenn die Laufzeitkonfiguration SQLite aktiviert (sqlite.enable=true), das Binärprogramm jedoch ohne SQLite-Unterstützung kompiliert wurde (NNG_SUPP_SQLITE nicht definiert).
Um dieses Thema prägnant zu halten, habe ich die wichtigsten Erkenntnisse unten zusammengefasst. Für die vollständige technische Aufschlüsselung (einschließlich detaillierter ASAN-Traces, Lebenszyklusdiagramme und Instrumentierungsprotokolle) verweise ich auf den angehängten Analysis_Report.md.
Mit AddressSanitizer habe ich die Quelle des ausgeleakten Speichers auf nni_msg_alloc innerhalb von tcptran_pipe_recv_cb (nng/src/sp/transport/mqtt/broker_tcp.c) eingegrenzt. Der Speicher wurde allokiert, aber nie vollständig freigegeben.
Ich habe den Referenzzählmechanismus von nni_msg analysiert. Ein normaler Lebenszyklus einer QoS-1-Nachricht sollte Folgendes umfassen:
Da GDB für dieses Szenario mit hoher Nebenläufigkeit unpraktikabel war, habe ich dynamische Instrumentierung verwendet, um den Lebenszyklus bestimmter Speicherobjekte zu verfolgen (z. B. 0x60e00002ffa0).
Das Ergebnis: Die Protokolle bestätigten eine Lebenszyklus-Diskrepanz:
nmq_pipe_send_start_v4 erzeugt wurde.Die Verfolgung des Ausführungsablaufs zeigte, warum die 3. Freigabe fehlte:
-DNNG_SUPP_SQLITE kompiliert, wodurch die Initialisierungslogik in nano_sock_setdb entfernt wurde. In nanomq.conf war jedoch sqlite.enable = true gesetzt. Dadurch blieb der db-Zeiger auf NULL.nni_qos_db_set eintritt, prüft die Funktion, ob der Zeiger NULL ist:// 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;
}
// ...
}
Die Funktion kehrt stillschweigend zurück, ohne nni_msg_free(msg) aufzurufen. Dies führt dazu, dass der msg-Zeiger unwiederbringlich verloren geht, wodurch der Speicherblock effektiv verwaist.
nni_qos_db_set. Die Funktion behandelt die Eigentümerschaft des msg-Zeigers nicht, wenn sie aufgrund eines ungültigen DB-Zustands eine vorzeitige Rückkehr ausführt.Empfehlungen:
nano_sock_setdb oder main-Funktion) eine Validierungslogik hinzu, um sicherzustellen, dass SQLite ordnungsgemäß läuft. Wenn conf->sqlite.enable == true erkannt wird, das NNG_SUPP_SQLITE-Makro jedoch nicht definiert ist oder SQLite abnormal ist, sollte das System entweder mit einer Fehlermeldung abbrechen oder enable zwangsweise auf false setzen und eine Warnung ausgeben.nni_qos_db_set Logik zur Ressourcenfreigabe hinzu. Wenn db == NULL ist, muss nni_msg_free(msg) aufgerufen werden, um die Referenzzählung auszugleichen und Speicherlecks zu verhindern.