####################################################################### Luigi Auriemma Applicazione: UFO2000 http://ufo2000.sourceforge.net Versioni: <= SVN 1057 Piattaforme: Windows, *nix, *BSD, Mac ed altre Bugs: A] buffer-overflow in recv_add_unit B] invalid memory access in decode_stringmap C] possible code execution through arrays D] SQL injection E] mapdata global buffer overflow Exploitation: A] remoto, contro client B] remoto, contro server C] remoto, contro client D] remoto, contro server E] remoto, contro client Data: 16 Jul 2006 Autore: Luigi Auriemma e-mail: aluigi@autistici.org web: aluigi.org ####################################################################### 1) Introduzione 2) Bugs 3) The Code 4) Fix ####################################################################### =============== 1) Introduzione =============== UFO2000 e' un gioco multiplayer a turni che si basa sulla serie X-COM. ####################################################################### ======= 2) Bugs ======= ----------------------------------- A] buffer-overflow in recv_add_unit ----------------------------------- Il comando utilizzato per aggiungere unita' (proprio il primo comando usato all'inizio della partita) e' vulnerabile ad un buffer-overflow che avviene durante la copia dei dati appena ricevuti nel buffer name grande soli 26 bytes. Da multiplay.cpp: int Net::recv_add_unit() { int num; char name[26]; int cost; pkt >> num; pkt >> name; ... -------------------------------------------- B] invalid memory access in decode_stringmap -------------------------------------------- Quando viene ricevuto un pacchetto il server chiama la funzione decode_stringmap che si occupa della lettura del numero di informazioni (keys e values) contenute nel blocco dati in ingresso e della successiva lettura di tali dati. Qui si verificano due problemi: - se i valori che indicano la grandezza dei dati sono invalidi cio' puo' portare alla lettura delle zone di memoria non allocate che si trovano dopo il pacchetto con il successivo crash (per esempio keysize dice di leggere 100 bytes mentre il pacchetto ne e' di soli 2) - il server termina se keysize o valsize sono troppo grandi e quindi non si puo' allocare la memoria quando viene chiamata la funzione resize Da server_transport.cpp: bool decode_stringmap(std::map &info, const void *buffer) { const unsigned char *p = (const unsigned char *)buffer; unsigned int num = decode_unsigned_int(p); while (num--) { unsigned int keysize = decode_unsigned_int(p); unsigned int valsize = decode_unsigned_int(p); std::string key; key.resize(keysize); std::string val; val.resize(valsize); for (unsigned int i = 0; i < keysize; i++) key[i] = decode_unsigned_char(p); for (unsigned int i = 0; i < valsize; i++) val[i] = decode_unsigned_char(p); info[key] = val; } return true; } ----------------------------------------- C] possible code execution through arrays ----------------------------------------- Alcuni comandi possono essere usati per far crashare il proprio client avversario attraverso l'utilizzo di valori invalidi (troppo grandi o negativi) per muoversi all'interno degli array del gioco. Un altro effetto e' la possibilita' di poter eseguire codice malevolo, infatti il gioco utilizza numeri molto grandi (solitamente a 32 bit con segno) che possono essere utilizzati per raggiungere qualsiasi zona della memoria, dopodiche' questi comandi permettono la scrittura di alcuni dati contenuti nel pacchetto all'interno di queste locazioni di memoria come avviene per esempio con "pkt >> scenario->rules[index]" dove il nostro numero a 32 bit (pkt >>) viene copiato dove vogliamo tramite il nostro index. Questi comandi sono recv_rules, recv_select_unit (select_unit controlla solo se num e' maggiore ma non se e' minore), recv_options e recv_unit_data (con un valore negativo o minore di 19). Da multiplay.cpp: int Net::recv_rules() { int index; pkt >> index; pkt >> scenario->rules[index]; ... ---------------- D] SQL injection ---------------- Il server fa' uso di un database SQL interno per la gestione degli account e di altre informazioni riguardo le partite. Nei punti in cui viene utilizzato l'input dell'utente con la stringa di formato %s anziche' %q potrebbe essere possibile immettere dei propri comandi SQL nella query preparata dal server. Da server_protocol.cpp: bool ServerClientUfo::recv_packet(NLuint id, const std::string &raw_packet) ... case SRV_GAME_REPLAY_REQUEST: { send_packet_back(SRV_GAME_RECOVERY_START, "1"); try { debug_game_id = atol(packet.c_str()); sqlite3::reader reader=db_conn.executereader("select command, packet_type, id from ufo2000_game_packets where game=%s order by id;", packet.c_str()); ... --------------------------------- E] mapdata global buffer overflow --------------------------------- mapdata e' un buffer globale dichiarato in main.cpp come una struttura di tipo GEODATA, ossia grande 56 bytes, che puo' essere portata all'overflow attraverso la funzione recv_map_data. L'effetto e' il crash immediato dell'avversario. Da multiplay.cpp: int Net::recv_map_data() { std::string map_name; std::string map_data; pkt >> map_name; pkt >> mapdata.x_size; pkt >> mapdata.y_size; pkt >> mapdata.z_size; pkt >> map_data; ASSERT((int)map_data.size() == mapdata.x_size * mapdata.y_size); memcpy(&mapdata.mapdata, map_data.data(), map_data.size()); .... ####################################################################### =========== 3) The Code =========== Proof-of-concept non disponibile. Inoltre ecco un'introduzione riguardo al funzionamento di server e client per capire il possibile sfruttamento di queste vulnerabilita'. Il server agisce semplicemente da luogo in cui tutti i clients possono incontrarsi e possono incominciare delle partite 1 contro 1. Il server potrebbe anche non permettere i login anonimi, utilizzando appunto gli account che richiedono username e password validi. In ogni partita il lavoro del server e' unicamente quello di fare il forwarding dei pacchetti tra i vari clients, quindi si puo' dire che quest'ultimi siano in una comunicazione virtualmente diretta tra di loro e le vulnerabilita' di cui sono affetti possono essere sfruttate sia da altri clients che dai servers. I pacchetti scambiati nella stanza principale (lobby) sono gestiti dal server (SRV_*) mentre quelli durante la partita dagli stessi clients (CMD_*). ####################################################################### ====== 4) Fix ====== Alcuni dei bugs piu' critici sono stati corretti gia' nel SVN 1058 mentre i rimanenti nella revision 1061. #######################################################################