Browse CTFs New CTF Sign in

Enumerating Local Services via mDNS PCAP Multicast Record Analysis and Host Fingerprinting

osint_collection Difficulty 1–5 30 min certifiable

Theory

Why This Matters

mDNS is the zero-configuration naming protocol used by Apple Bonjour, Avahi on Linux, and Windows mDNS to make devices discoverable on local networks without a DNS server. In CTF challenges and real-world investigations, mDNS traffic in a PCAP reveals the complete local network topology: device hostnames, service types, IP addresses, and metadata encoded in TXT records — all broadcast in cleartext. Attackers and penetration testers use mDNS enumeration (via tools like avahi-browse or Responder) to identify high-value targets before any active scanning. Forensic analysts use the same traffic to reconstruct what devices were present and what services they advertised.

Core Concept

mDNS operates on UDP port 5353 to the multicast address 224.0.0.251 (IPv4) or ff02::fb (IPv6). It uses the standard DNS message format but with link-local multicast instead of unicast to a DNS server. A device can resolve hostnames and discover services without any infrastructure.

DNS-SD (DNS Service Discovery) rides on top of mDNS to advertise and discover services. The discovery process uses three record types:

  • PTR records: map service type to instance name. E.g., _http._tcp.local PTR MyWebServer._http._tcp.local. A PTR query to _services._dns-sd._udp.local returns all advertised service types.
  • SRV records: map service instance to hostname and port. E.g., MyWebServer._http._tcp.local SRV 0 0 80 mydevice.local.
  • TXT records: carry key=value metadata for the service instance. E.g., path=/index.html, version=1.0, admin=true.

From mDNS traffic alone, an analyst can enumerate: device hostnames (A/AAAA records), IP addresses, service types running on each device, port numbers, and service-specific metadata in TXT records.

Technical Deep-Dive

# Display all mDNS traffic from a PCAP
tshark -r capture.pcap -Y "mdns" 
  -T fields 
  -e frame.number -e frame.time_relative 
  -e ip.src -e dns.qry.name -e dns.resp.name 
  -e dns.ptr.domain_name -e dns.srv.name 
  -e dns.a -e dns.txt 
  -E header=y

# Filter mDNS to multicast destination only (announcements)
tshark -r capture.pcap 
  -Y "udp.dstport == 5353 and ip.dst == 224.0.0.251" 
  -T fields 
  -e frame.time_relative -e ip.src 
  -e dns.resp.type -e dns.resp.name -e dns.a 
  -e dns.ptr.domain_name -e dns.srv.target 
  -E header=y

# Extract all PTR records (service type → instance name mappings)
tshark -r capture.pcap -Y "mdns and dns.ptr.domain_name" 
  -T fields -e ip.src -e dns.resp.name -e dns.ptr.domain_name 
  | sort -u

# Extract all SRV records (instance → host + port)
tshark -r capture.pcap -Y "mdns and dns.srv.target" 
  -T fields 
  -e ip.src -e dns.resp.name 
  -e dns.srv.target -e dns.srv.port 
  | sort -u

# Extract TXT record key=value metadata
tshark -r capture.pcap -Y "mdns and dns.txt" 
  -T fields -e ip.src -e dns.resp.name -e dns.txt 
  | sort -u
#!/usr/bin/env python3
"""
Parse mDNS PCAP and build a per-host service inventory.
Requires: pip install scapy
"""
from scapy.all import rdpcap, DNS, DNSRR, DNSRRSRV, DNSRRTXT, UDP
from collections import defaultdict

packets = rdpcap("capture.pcap")
hosts = defaultdict(lambda: {"services": [], "ips": set(), "txt": []})

for pkt in packets:
    if not (pkt.haslayer(UDP) and pkt[UDP].dport == 5353 and pkt.haslayer(DNS)):
        continue
    dns = pkt[DNS]
    src_ip = pkt["IP"].src if "IP" in pkt else "?"

    # Walk answer, authority, and additional records
    for section in (dns.an, dns.ns, dns.ar):
        rr = section
        while rr and rr.type != 0:
            name = rr.rrname.decode(errors="replace").rstrip(".")
            if rr.type == 12:   # PTR
                ptr = rr.rdata.decode(errors="replace").rstrip(".")
                hosts[src_ip]["services"].append(f"PTR: {name} -> {ptr}")
            elif rr.type == 33:  # SRV
                hosts[src_ip]["services"].append(
                    f"SRV: {name} -> {rr.target.decode(errors='replace')}:{rr.port}")
            elif rr.type == 1:   # A
                hosts[src_ip]["ips"].add(rr.rdata)
            elif rr.type == 16:  # TXT
                for s in rr.rdata:
                    hosts[src_ip]["txt"].append(s.decode(errors="replace"))
            try:
                rr = rr.payload
            except Exception:
                break

for src, info in sorted(hosts.items()):
    print(f"
[{src}]")
    for ip in sorted(info["ips"]):
        print(f"  A: {ip}")
    for svc in sorted(set(info["services"])):
        print(f"  {svc}")
    for txt in sorted(set(info["txt"])):
        print(f"  TXT: {txt}")
# Live enumeration reference (for understanding what tools generate these captures):
# avahi-browse -a -t          # enumerate all services on the local network
# dns-sd -B _services._dns-sd._udp local   # macOS service discovery
# python3 -m zeroconf          # Python zeroconf library discovery

Analytical Methodology

  1. Apply Wireshark filter mdns or udp.port == 5353. Count the number of unique source IPs to estimate the number of active devices on the local segment during the capture window.
  2. For each source IP, examine the PTR records it announces. PTR records with names ending in ._tcp.local or ._udp.local reveal service types. Compile a list: _http._tcp, _smb._tcp, _afpovertcp._tcp, _ssh._tcp, etc.
  3. For each service instance (PTR target), locate the corresponding SRV record to obtain the canonical hostname and TCP/UDP port number. The hostname resolves to an IP via the A/AAAA record in the same mDNS response.
  4. Examine TXT records for each service instance. TXT records carry service-specific metadata: web root path, version numbers, authentication requirements, device model, and sometimes credentials or access tokens in IoT devices.
  5. Build a host inventory table: source IP, mDNS hostname (.local name), resolved IP (from A record), advertised service types, ports, and TXT metadata. This is the network topology derived from passive mDNS observation.
  6. Identify Apple Bonjour publishers by service types _airplay._tcp, _raop._tcp, _homekit._tcp, _afpovertcp._tcp. Identify Avahi (Linux) by _workstation._tcp. Identify Windows mDNS by _smb._tcp and _microsoft-ds._tcp.
  7. Look for mDNS probing conflicts — two hosts sending mDNS probes (questions, not answers) for the same hostname. This occurs when two devices attempt to claim the same .local name and indicates hostname collision, possibly suggesting a rogue device.
  8. Correlate hostnames and IPs discovered via mDNS with other traffic in the PCAP (HTTP, SMB, SSH sessions) to confirm which services were actually used versus merely advertised.

Common Analytical Errors

  • Missing IPv6 mDNS: mDNS uses ff02::fb for IPv6 multicast. If the capture includes IPv6 traffic, filter udp.dstport == 5353 rather than relying on the mdns display filter, which may not match IPv6 mDNS in all Wireshark versions.
  • Confusing mDNS queries with responses: mDNS uses the QR bit in the DNS header: 0 = query (device looking for services), 1 = response (device announcing services). Responses are the data source for enumeration; queries reveal what services a device is looking for.
  • Overlooking "goodbye" packets: When a device leaves the network, it sends a final mDNS announcement with TTL=0 (a "goodbye" packet) to withdraw its records. These packets indicate a device went offline — relevant for timeline reconstruction.
  • Assuming .local hostnames are unique: mDNS hostname conflicts are common in enterprise networks. Multiple devices may claim similar hostnames, and the A records associated with the same hostname may change over the capture window. Always correlate hostname with the source IP of the announcement, not just the hostname string.

NICE Framework Alignment

Code Knowledge/Skill/Task Statement How This Card Develops It
K0046 Knowledge of intrusion detection systems and methodologies mDNS enumeration is a passive reconnaissance technique detectable by monitoring multicast traffic
K0093 Knowledge of network protocols mDNS and DNS-SD protocol structure: PTR, SRV, TXT record semantics and multicast addressing
K0221 Knowledge of OSI model and network layers mDNS operates at layer 7 (DNS format) over UDP layer 4 using link-local multicast at layer 3
S0046 Skill in performing packet-level analysis Parsing mDNS PTR/SRV/TXT records from PCAP to construct a host-service inventory
T0023 Collect intrusion artifacts for use in forensic analysis mDNS-derived device inventory is a forensic artifact establishing what hosts were present on the network segment

Further Reading

  • RFC 6762: Multicast DNS — complete mDNS specification
  • RFC 6763: DNS-Based Service Discovery — DNS-SD PTR/SRV/TXT protocol details
  • Apple Bonjour technology overview (developer.apple.com) — service type registry
  • Avahi project documentation (avahi.org) — Linux mDNS implementation reference
  • Responder tool documentation — attacker mDNS/LLMNR poisoning to understand what anomalous mDNS traffic looks like

Challenge Lab

Reinforce your learning with a hands-on generated challenge based on this card's competency.