本仓库包含 CVE-2026-36590 的公开公告和技术分析,这是一项影响 EMQ NanoMQ v0.24.9 的拒绝服务漏洞。
本仓库公开旨在为 CVE 审核提供可访问的技术参考。CVE-2026-36590 的最终分类将由 CVE 团队或相关 CNA 决定。
NanoMQ 团队对该问题提出异议,并认为其与配置相关。本仓库侧重于技术证据,包括复现材料、ASAN 结果、运行时日志和源码分析。
/nanomq/nng/src/supplemental/mqtt/mqtt_qos_db_api.c / nni_qos_db_set当 NanoMQ v0.24.9 在没有 SQLite 支持的情况下编译,而 sqlite.enable 被配置为 true 时,QoS 数据库指针可能保持为 NULL。在 MQTT QoS 消息处理过程中,nni_qos_db_set 可能提前返回而不释放克隆的消息引用。重复的 QoS > 0 MQTT PUBLISH 消息可能导致持续的内存消耗,最终导致拒绝服务。
该问题依赖于非默认的运行时配置,但以下事实表明为何仍需进一步审核:
NanoMQ 接受该运行时配置并继续运行,而不是报告当前构建中不支持 SQLite。
这种配置不匹配在实际中是可能发生的。用户可能使用常规构建命令编译 NanoMQ,但之后从示例配置文件中复制或启用了 SQLite 持久化部分。
受影响的代码路径未完全验证这种不一致的状态。当运行时配置将 SQLite 视为已启用但数据库指针为 NULL 时,nni_qos_db_set() 提前返回而不释放传入的消息对象。
一旦该配置被接受,此问题即可由远程 MQTT 流量触发。重复的 QoS > 0 PUBLISH 消息可进入受影响的路径并导致累积的内存泄漏。
使用 1、50 和 500 轮复现的 ASAN 测试表明,泄漏的字节数和分配次数随触发尝试次数增加而增加。这表明该问题与输入数量相关,而非一次性的小泄漏。
用户应限制对 NanoMQ 实例的访问,避免将 MQTT 服务暴露给不受信任的客户端,并避免在不支持 SQLite 的构建中启用 SQLite 持久化。厂商应在 nni_qos_db_set 的 db == NULL 分支中添加 SQLite 配置的启动验证,并释放消息引用。
Docker/Docker_Reproduction_Guide.md摘要 本仓库包含针对 NanoMQ 中发现的内存泄漏漏洞的概念验证(PoC)和详细分析。该问题允许远程攻击者通过特定的 QoS MQTT 消息耗尽系统内存,从而造成拒绝服务(DoS)。
我对 NanoMQ 中的内存泄漏现象进行了深入分析。通过动态插桩和代码审查,我确定了一条关键的资源泄漏路径,该路径在**运行时配置启用了 SQLite(sqlite.enable=true)但二进制文件编译时未包含 SQLite 支持(NNG_SUPP_SQLITE 未定义)**时被触发。
为保持本文简洁,我在下方总结了关键发现。如需完整的技术分解(包括详细的 ASAN 跟踪、生命周期图和插桩日志),请参阅附件 Analysis_Report.md。
使用 AddressSanitizer,我将泄漏内存的来源定位到 tcptran_pipe_recv_cb(nng/src/sp/transport/mqtt/broker_tcp.c)中的 nni_msg_alloc。该内存被分配后从未被完全释放。
我分析了 nni_msg 的引用计数机制。正常的 QoS 1 消息生命周期应包括:
由于 GDB 在这种高并发场景下不实用,我使用动态插桩来跟踪特定内存对象(例如 0x60e00002ffa0)的生命周期。
发现: 日志确认了生命周期不匹配:
nmq_pipe_send_start_v4 中生成的克隆 2。跟踪执行流程揭示了第 3 次释放缺失的原因:
-DNNG_SUPP_SQLITE,导致 nano_sock_setdb 中的初始化逻辑被剔除。然而,nanomq.conf 中设置了 sqlite.enable = true。这导致 db 指针保持为 NULL。nni_qos_db_set 进行存储时,该函数会检查 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;
}
// ...
}
该函数静默返回,未调用 nni_msg_free(msg)。这导致 msg 指针不可挽回地丢失,实际上使内存块成为孤儿内存。
nni_qos_db_set 中缺乏防御性编程。当由于无效的数据库状态而执行提前返回时,它没有处理 msg 指针的所有权。建议:
nano_sock_setdb 或 main 函数)添加验证逻辑,以确保 SQLite 正常运行。如果检测到 conf->sqlite.enable == true 但 NNG_SUPP_SQLITE 宏未定义或 SQLite 异常,系统应报错退出,或强制将 enable 设置为 false 并打印警告。nni_qos_db_set 函数的提前返回分支中添加资源释放逻辑。如果 db == NULL,必须调用 nni_msg_free(msg) 以平衡引用计数并防止内存泄漏。