Subject: Re: traceroute as a flooder (fwd)
To: NetBSD-current Users <current-users@netbsd.org>
From: Curt Sampson <cjs@cynic.net>
List: current-users
Date: 02/16/1999 15:32:00
I've just sent this message to BUGTRAQ with a fix for the traceroute
problems brought up there recently. If someone wants to, we should
do a security advisory about this. I don't have a 1.3.3 machine to
test these patches on, however.

cjs
--
Curt Sampson  <cjs@cynic.net>   604 801 5335   De gustibus, aut bene aut nihil.
The most widely ported operating system in the world: http://www.netbsd.org

---------- Forwarded message ----------
Date: Tue, 16 Feb 1999 15:30:13 -0800 (PST)
From: Curt Sampson <cjs@cynic.net>
To: BUGTRAQ@netspace.org
Cc: Dag-Erling Smorgrav <des@IFI.UIO.NO>
Subject: Re: traceroute as a flooder


I've appended the set of patches for the traceroute bugs that I
just 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.

I believe that my solution for #2 is better than the one recently
committed to FreeBSD because mine 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. (I don't believe 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.)
Also, I don't arbitrarially limit the timeout to a day, though I
doubt that makes much difference to anyone.

cjs
--
Curt Sampson  <cjs@cynic.net>   604 801 5335   De gustibus, aut bene aut nihil.
The most widely ported operating system in the world: http://www.netbsd.org


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);
 }