back
1 comments
Can confirm that this is a serious problem that should not be taken lightly.

Message compression is designed in a way that it relies on one (or two) bytes that represent the length of a string encoding. That means that labels of domains are encoded in a way that they have a length (of max 64 bytes length each) to encode them:

Example: example.com will be 3example3com0, foo.bar.de will be 3foo3bar2de0.

... whereas the trailing null byte represents the "root" and is the terminator for the labels, meaning that the domain string ended.

The problem is now that message compression in DNS relies on actually two bytes, not one, that represent this. And they can appear every time a length can appear, which means that partial recursive loops for the same "domain labels" is possible, too. The additionally IMO flawed concept here is that DNS has lots of 2 byte numbers and lots of 1 byte numbers that don't make sense at all.

If the length is basically > 64 (and starts with 0b11000000 which means it has to be actually > 192), it is a byte-based pointer which is represented with 2 bytes minus the leading two bits.

The pointer is the byte in the raw DNS packet, which means there can be labels that have no trailing NULL byte, but include another pointer which leads to another pointer etc. pp. And I'll let you decide what probably happens if the value of the length byte is > 64 and < 192, because as it turns out, this is also not implemented correctly across a lot of projects I've taken a look at the last couple weeks.

= = = = =

This problem could've been easily avoided in a better DNS protocol design, where domains have a static-size maximum value lengths (not dynamic lengths) and where something like gzip can be used on top in order to reduce message size. If a protocol is designed in a way that you have to parse everything ahead, including the dynamic domain pointer/length mixup that is part of the header of a record, and afterwards appear fixed-size bytes that represent e.g. TTL, class (which is internet anyways) and the RDATA length which is static, but where the following record data also can contain pointers ... you have to start to realize this is the wrong approach.

I mean, it could've been easily a dictionary that appears as part of the header first, and every other instance has to point to the index and default to NULL (the root).

I also think there will also be a second wave of this flaw that will exploit DNSSEC cookies (that appear later in the additional/authorative packet section) using those pointers, as you could craft a packet that fakes DNSSEC values for wrong domains, and naive recursive DNS implementations are probably failing in this scenario. As I mentioned most implementations fail the ranges between 64 and 192 for the length byte, and crafting a domain query that exploits this is very easily done.

Anyways, just my two cents as I've been implementing exactly these parts of the DNS protocol with a pregenerated dictionary to avoid pointer problems in the last couple weeks; for my peer-to-peer Web Browser project.