Публичное уведомление и технический анализ CVE-2026-36590, уязвимости типа отказ в обслуживании в NanoMQ v0.24.9.
Этот репозиторий содержит публичное уведомление и технический анализ для 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 может вернуться рано, не освободив клонированную ссылку на сообщение. Повторяющиеся MQTT PUBLISH-сообщения с QoS > 0 могут вызвать непрерывное потребление памяти, что в конечном итоге приведёт к отказу в обслуживании.
Проблема зависит от нестандартной конфигурации выполнения, но следующие факты показывают, почему она всё ещё требует дальнейшего рассмотрения:
NanoMQ принимает конфигурацию выполнения и продолжает работу, вместо того чтобы сообщить, что поддержка SQLite недоступна в текущей сборке.
Такое несоответствие конфигурации может произойти на практике. Пользователь может собрать NanoMQ обычной командой сборки, а затем скопировать или включить раздел сохранения SQLite из примера конфигурационного файла.
Затронутый путь кода не fully проверяет это неконсистентное состояние. Если конфигурация выполнения считает SQLite включённым, но указатель базы данных равен NULL, nni_qos_db_set() возвращается рано, не освобождая переданный объект сообщения.
После того как эта конфигурация принята, проблема может быть вызвана удалённым MQTT-трафиком. Повторяющиеся PUBLISH-сообщения с QoS > 0 могут попасть на затронутый путь и вызвать накопленные утечки памяти.
Тесты ASAN с 1, 50 и 500 раундами воспроизведения показывают, что количество утёкших байт и выделений увеличивается с числом попыток триггера. Это говорит о том, что проблема зависит от количества входных данных, а не является одноразовой небольшой утечкой.
Пользователям следует ограничить доступ к экземплярам NanoMQ, избегать предоставления MQTT-сервисов ненадёжным клиентам и не включать сохранение SQLite в сборках без поддержки SQLite. Вендору следует добавить проверку конфигурации SQLite при запуске и освобождать ссылку на сообщение в ветви db == NULL функции nni_qos_db_set.
Docker/Docker_Reproduction_Guide.mdКраткое описание Этот репозиторий содержит Proof of Concept (PoC) и подробный анализ уязвимости утечки памяти, найденной в NanoMQ. Проблема позволяет удалённым злоумышленникам вызвать отказ в обслуживании (DoS) путём истощения системной памяти через определённые MQTT-сообщения QoS.
Я провёл углублённый анализ явления утечки памяти в Nanomq. С помощью динамической инструментации и рецензирования кода я выявил критический путь утечки ресурсов, который возникает, когда конфигурация выполнения включает SQLite (sqlite.enable=true), но бинарный файл скомпилирован без поддержки SQLite (NNG_SUPP_SQLITE не определён).
Чтобы сохранить краткость этой проблемы, я обобщил ключевые выводы ниже. Полный технический разбор (включая подробные трассировки ASAN, диаграммы жизненного цикла и журналы инструментации) см. в приложенном файле Analysis_Report.md.
Используя AddressSanitizer, я определил источник утечки памяти в nni_msg_alloc внутри tcptran_pipe_recv_cb (nng/src/sp/transport/mqtt/broker_tcp.c). Память выделялась, но никогда полностью не освобождалась.
Я проанализировал механизм подсчёта ссылок nni_msg. Нормальный жизненный цикл сообщения QoS 1 должен включать:
Поскольку GDB был непрактичен для этого сценария высокой степени параллелизма, я использовал динамическую инструментацию для трассировки жизненного цикла конкретных объектов памяти (например, 0x60e00002ffa0).
Результат: Журналы подтвердили несоответствие жизненного цикла:
nmq_pipe_send_start_v4.Трассировка потока выполнения показала, почему отсутствовало 3-е освобождение:
-DNNG_SUPP_SQLITE, из-за чего логика инициализации в nano_sock_setdb была удалена. Однако в nanomq.conf было установлено sqlite.enable = true. Это привело к тому, что указатель db остался NULL.nni_qos_db_set для сохранения, функция проверяет нулевой указатель:// File: /nanomq/nng/src/supplemental/mqtt/mqtt_qos_db_api.c
void nni_qos_db_set(..., void *db, ..., nng_msg *msg) {
if (db == NULL) {
// КРИТИЧЕСКАЯ ОШИБКА:
// Ранний возврат происходит, потому что db == NULL.
// Функция получает владение 'msg' (Ref++), но не освобождает его.
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), чтобы сбалансировать счётчик ссылок и предотвратить утечку памяти.