Problem
_read_bitmap(end) reads an attacker-controlled bitmap_length byte and then unconditionally does self.offset += 2 + bitmap_length, with no check that offset_plus_two + bitmap_length <= end. Because Python slicing is forgiving, the inner self.data[offset_plus_two:bitmap_end] does not raise even when bitmap_end overshoots — it simply consumes adjacent bytes (the next record's header) as bitmap data and then leaves self.offset past the NSEC record's declared end. _read_others only resets self.offset = end in the exception path, so a malformed-but-non-throwing NSEC silently corrupts the parse offset for every subsequent record in the same packet, and the bitmap window/byte arithmetic can convert two random adjacent bytes into hundreds of rdtypes entries that get cached on the resulting DNSNsec.
Why This Matters
Single-packet impact only (record-boundary confusion + a few KB of synthesized rdtypes cached as long as the NSEC stays in the DNSCache), but it lets a malicious LAN peer suppress legitimate records in a packet (subsequent records fail to parse or are skipped) and pollute the NSEC cache with attacker-chosen "absent" type bits. That second effect can be used to talk an AddressResolver out of issuing follow-up queries for a real host, since NSEC negative answers gate that decision.
Suggested Fix
After reading bitmap_length, clamp / validate against end: if offset_plus_two + bitmap_length > end: raise IncomingDecodeError(...). Also validate window < 256 (it already fits in a byte) and bound the total rdtypes length so a 255-byte window can't compose with multiple windows to bloat the in-memory cached record.
Details
|
|
| Severity |
🟡 Medium |
| Category |
memory_safety |
| Location |
src/zeroconf/_protocol/incoming.py:383-399 |
| Effort |
⚡ Quick fix |
🤖 Created by Kōan from audit session
Problem
_read_bitmap(end)reads an attacker-controlledbitmap_lengthbyte and then unconditionally doesself.offset += 2 + bitmap_length, with no check thatoffset_plus_two + bitmap_length <= end. Because Python slicing is forgiving, the innerself.data[offset_plus_two:bitmap_end]does not raise even whenbitmap_endovershoots — it simply consumes adjacent bytes (the next record's header) as bitmap data and then leavesself.offsetpast the NSEC record's declaredend._read_othersonly resetsself.offset = endin the exception path, so a malformed-but-non-throwing NSEC silently corrupts the parse offset for every subsequent record in the same packet, and the bitmap window/byte arithmetic can convert two random adjacent bytes into hundreds ofrdtypesentries that get cached on the resultingDNSNsec.Why This Matters
Single-packet impact only (record-boundary confusion + a few KB of synthesized
rdtypescached as long as the NSEC stays in the DNSCache), but it lets a malicious LAN peer suppress legitimate records in a packet (subsequent records fail to parse or are skipped) and pollute the NSEC cache with attacker-chosen "absent" type bits. That second effect can be used to talk anAddressResolverout of issuing follow-up queries for a real host, since NSEC negative answers gate that decision.Suggested Fix
After reading
bitmap_length, clamp / validate againstend:if offset_plus_two + bitmap_length > end: raise IncomingDecodeError(...). Also validatewindow < 256(it already fits in a byte) and bound the totalrdtypeslength so a 255-byte window can't compose with multiple windows to bloat the in-memory cached record.Details
src/zeroconf/_protocol/incoming.py:383-399🤖 Created by Kōan from audit session