I am not aware of any known DoS attacks against the label decoding/compression trick that DNS uses (references perhaps?). That said, you can design a rule to detect the presence of a compression pointer using
byte_test as such:
byte_test:2,&,0xc000,0;. This uses a bitwise AND to mask out all but the two highest bits and see if they are both '11', which is the marker for a compression pointer (the remaining 14 bits specify the offset from the start of the payload to the referenced DNS label). If that byte_test returns true, you've detected such a pointer, and you can then build the rest of the rule around that. It's up to you to walk the DNS packet until you arrive at the place where you expect such a pointer to exist.
Have a look over the DNS protocol for any further oddities of this clever trick.
Thinking about it, I think it's really only possible with the second byte of the packet. Unfortunately, Snort resets the
doe_ptr to the start of the payload with each new rule option unless you specify a
relative to any of the
byte_* options (I think). I do not believe that one byte option can be relative to a previous byte option, only to content matches.
So, given that 99% of your DNS packets will never use the remaining 6 bits of the second high byte, you can start by looking for the hex value
0xc0, which in binary is
11000000. This can replace the first
alert udp any 53 -> any 1024: (msg:"DNS recursive Ptr test"; content:"|c0|"; byte_extract:1,0,dns_comp_ptr,relative,little; byte_test:1,=,dns_cmp_ptr,dns_comp_ptr,little; sid:12345; rev:1; classtype:yadda-yadda;)
content:"|c0|"; - Find a compression pointer.
byte_extract:1,0,dns_comp_ptr,relative,little; - grab the next byte and store it to the variable
dns_comp_ptr (untested! My offset may be incorrect -- might need to add +1).
byte_test:1,=,dns_cmp_ptr,dns_comp_ptr,little; - test one byte and see if it is equal to the value stored in
dns_comp_ptr, after jumping the value indicated by
dns_comp_ptr, in little-endian format (which is how we see it as displayed in Wireshark).
If the last
byte_test returns true, then the rest of the rule will validate and alert. All in theory, of course. Totally untested and I offer no guarantees that it will work. But it encompasses the basic idea of needing to check every compression pointer in a DNS response (with a response on a Google domain, expect several of these) to see if that pointer points back to itself by following it at least once.
The only way, that I can think of anyways, to test both bytes, is you would need a way to jump the
doe_ptr to the byte immediately prior to the compression pointer, i.e., via a content. Snort doesn't have a rule option that directly says to move the
doe_ptr by a specified value all by itself (though that really would be a cool idea, TBH).
Once you have the
doe_ptr placed properly, you use
byte_test:2,&,0xc000,1,relative,little; to mask out all bits except the first two and see if they match the hex
0xc000. Then you do
byte_extract:2,0,dns_comp_ptr,little,bitwiseand 0x3fff; then
byte_test:2,=,dns_comp_ptr,dns_comp_ptr,little;. Again untested, so my offsets are probably wrong. I forget how Snort twiddles with the byte placement precisely.
bitwiseand 0x3fff thingy is my own creation.
byte_extract currently has no way to do bitmasking (which is a bitwise AND operation) to only extract out a few bits in a byte. I made the operand up.
PS, I have no packets available to actually test this. Maybe I can hack one together at some point and try it out.