GHSA-mxwc-wh95-pw4g Trapster Community: Unauthenticated malformed DNS compression pointers crash per-packet honeypot handler
Summary`trapster.libs.dns.decode_labels()` decodes DNS names from attacker-supplied UDP packets and recurses **once per RFC 1035 compression pointer** with **no cycle detection and no depth bound**. A single unauthenticated UDP datagram sent to the DNS honeypot drives the function past CPython's recursion limit, raising `RecursionError`. That exception is not handled anywhere on the `datagram_received` path, so it escapes into the asyncio event loop's default exception handler, the per-packet proxy task is never created, and a low-rate flood produces sustained CPU burn and log flooding (denial of service of the DNS honeypot module).SeverityMedium (CVSS:3.1 `AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L`). Network-reachable, unauthenticated, single-packet, availability-only against the DNS honeypot listener.Affected componentFile: `trapster/libs/dns.py`, function `decode_labels()` (called by `decode_question_section` → `decode_dns_message`).Reached from: `trapster/modules/dns.py`, `DnsUdpProtocol.datagram_received()` → `dns.decode_dns_message(data)`, where `data` is the raw attacker UDP payload received by `DnsHoneypot` on its configured bind address/port.Version tested: latest `main` at commit `23156739de23816657cbc4582ad32094ed1cab43`.Details`decode_labels` implements RFC 1035 §4.1.4 name compression:Each compression pointer triggers a fresh recursive call to `decode_labels`. There is:**No cycle detection** — a pointer that targets its own offset recurses forever.**No depth bound** — a chain of distinct forward pointers recurses once per pointer.Either shape exhausts the Python stack and raises `RecursionError`. `decode_dns_message` does not catch it, and in `DnsUdpProtocol.datagram_received` the call `dns.decode_dns_message(data)` is unguarded, so the exception propagates out of `datagram_received` into the event loop. Each hostile packet therefore aborts its own packet-handling/proxy task, and the loop's default exception handler logs a full traceback for every packet.This is the same class of bug fixed upstream in `python-zeroconf` (compression-pointer recursion), except this implementation additionally lacks the loop/cycle guard that zeroconf already had.Proof of ConceptReal-deploy E2E. The actual `trapster.modules.dns.DnsHoneypot` server is started on a real UDP socket; hostile packets are sent from a separate real client socket over loopback. The event-loop exception handler records what escapes `datagram_received`.`e2e_poc.py`:Verbatim run against the deployed honeypot at commit `23156739de23816657cbc4582ad32094ed1cab43`:The negative control (a well-formed `example.com` A query) parses with zero exceptions, confirming the crash is specific to the malformed compression input.ImpactAny host able to send UDP to the DNS honeypot's bind address/port (unauthenticated, no UI) can crash the per-packet handler with a single ~18-byte datagram. A low-rate flood (a few packets per second) keeps the event loop logging full tracebacks and burning CPU, degrading the DNS honeypot and its logging pipeline. Availability impact only; no memory disclosure or code execution.Suggested fixMake `decode_labels` iterative-bounded: require every compression pointer to point strictly backward to a not-yet-visited offset, which bounds both cycles and long forward chains in O(message length) with no recursion. (This mirrors `dnspython`'s `biggest_pointer` design and the zeroconf depth-bound fix.) Replace the bare `raise "unknown label encoding"` with a real exception, and consider wrapping `dns.decode_dns_message(data)` in `DnsUdpProtocol.datagram_received` in a try/except so a malformed packet is logged once rather than escaping to the loop handler.Verified fix (benign + legitimate backward-compression names still decode; both hostile vectors are bounded to `ValueError` with no recursion):A fix PR will be supplied from a temporary private fork during the embargo.CreditDiscovered and reported by tonghuaroot.
CVSS v3.1 score: 5.3