####################################################################### Luigi Auriemma Applicazione: Freeciv http://www.freeciv.org Versioni: <= 2.1.0-beta1 e SVN <= 15 Jul 2006 Piattaforme: Windows, *nix, *BSD, MacOS ed altre Bugs: A] memcpy crash in generic_handle_player_attribute_chunk B] invalid memory access in handle_unit_orders Exploitation: remote, versus server Data: 23 Jul 2006 Autore: Luigi Auriemma e-mail: aluigi@autistici.org web: aluigi.org ####################################################################### 1) Introduzione 2) Bugs 3) The Code 4) Fix ####################################################################### =============== 1) Introduzione =============== Freeciv e' un clone open source del famoso gioco di strategia Civilization. Il gioco supporta anche il gioco online attraverso il suo stesso metaserver (visibile anche da web) e GGZ (http://www.ggzgamingzone.org) ####################################################################### ======= 2) Bugs ======= -------------------------------------------------------- A] memcpy crash in generic_handle_player_attribute_chunk -------------------------------------------------------- handle_player_attribute_chunk (che punta a generic_handle_player_attribute_chunk) e' una funzione usata sia dal cliente che dal server quando viene ricevuto un pacchetto di tipo PACKET_PLAYER_ATTRIBUTE_CHUNK. La funzione si comporta come un assemblatore di dati per un buffer allocato di massimo 262144 bytes. Esistono due problemi in questa funzione: - la lunghezza del chunk ricevuto (chunk_length) non e' verificata quindi usando un valore negativo un attacker puo' bypassare il controllo iniziale e copiare una quantita' enorme di dati ((unsigned)chunk_length) nel buffer data con il conseguente crash - "chunk->offset + chunk->chunk_length > chunk->total_length" puo' essere bypassato utilizzando un numero positivo molto grande come 0x7fffffff che permette la copia di dati dal nostro pacchetto alla memoria situata all'offset malforme del buffer allocato. Non sembra possibile eseguire codice malevolo con questo bug in quanto la memoria di destinazione di solito e' invalida Da common/packets.c: void generic_handle_player_attribute_chunk(struct player *pplayer, const struct packet_player_attribute_chunk *chunk) { freelog(LOG_DEBUG, "received attribute chunk %d/%d %d", chunk->offset, chunk->total_length, chunk->chunk_length); if (chunk->total_length < 0 || chunk->total_length >= MAX_ATTRIBUTE_BLOCK || chunk->offset < 0 || chunk->offset + chunk->chunk_length > chunk->total_length || (chunk->offset != 0 && chunk->total_length != pplayer->attribute_block_buffer.length)) { /* wrong attribute data */ if (pplayer->attribute_block_buffer.data) { free(pplayer->attribute_block_buffer.data); pplayer->attribute_block_buffer.data = NULL; } pplayer->attribute_block_buffer.length = 0; freelog(LOG_ERROR, "Received wrong attribute chunk"); return; } /* first one in a row */ if (chunk->offset == 0) { if (pplayer->attribute_block_buffer.data) { free(pplayer->attribute_block_buffer.data); pplayer->attribute_block_buffer.data = NULL; } pplayer->attribute_block_buffer.data = fc_malloc(chunk->total_length); pplayer->attribute_block_buffer.length = chunk->total_length; } memcpy((char *) (pplayer->attribute_block_buffer.data) + chunk->offset, chunk->data, chunk->chunk_length); ... ---------------------------------------------- B] invalid memory access in handle_unit_orders ---------------------------------------------- La funzione del server handle_unit_orders non verifica la lunghezza massima del valore packet->length che non dovrebbe mai essere maggiore di 2000 (MAX_LEN_ROUTE) mentre e' possibile per un attacker utilizzare qualsiasi numero positivo. Il crash potrebbe richiedere diversi tentativi (solitamente 3) prima di manifestarsi. Da server/unithand.c: void handle_unit_orders(struct player *pplayer, struct packet_unit_orders *packet) { struct unit *punit = player_find_unit_by_id(pplayer, packet->unit_id); struct tile *src_tile = map_pos_to_tile(packet->src_x, packet->src_y); int i; if (!punit || packet->length < 0 || punit->activity != ACTIVITY_IDLE) { return; } if (src_tile != punit->tile) { /* Failed sanity check. Usually this happens if the orders were sent * in the previous turn, and the client thought the unit was in a * different position than it's actually in. The easy solution is to * discard the packet. We don't send an error message to the client * here (though maybe we should?). */ return; } for (i = 0; i < packet->length; i++) { ... ####################################################################### =========== 3) The Code =========== Proof-of-concept non disponibile, bisogna modificare il codice sorgente del client per forzare l'invio di dati malformati. ####################################################################### ====== 4) Fix ====== I bug sono stati corretti nel SVN il 16 Jul 2006 #######################################################################