####################################################################### Luigi Auriemma Applicazione: Lithtech engine (nuovo protocollo di rete) http://www.lithtech.com Giochi: Contract Jack <= 1.1 F.E.A.R. <= 1.01 / 1.02 No one lives forever 2 <= 1.3 Tron 2.0 <= 1.042 ... altri? Piattaforme: Windows e Mac Bug: socket irraggiungibile Exploitation: remoto, contro il server Date: 13 December 2004 Author: Luigi Auriemma e-mail: aluigi@autistici.org web: aluigi.org ####################################################################### 1) Introduzione 2) Bug 3) The Code 4) Fix ####################################################################### =============== 1) Introduzione =============== Il Lithtech engine e' un motore di gioco utilizzato da molti giochi. Alcuni dei giochi rilasciati recentemente e basati su tale motore usano un protocollo di rete differente rispetto a tutti gli altri (probabilmente usano una nuova versione del motore ma naturalmente non sono a conoscenza di tutti questi dettagli). Proprio questi giochi recenti (tutti sviluppati da Monolith) sono quelli affetti dal bug che mi accingo a descrivere: Contract Jack (Nov 2003), No one lives forever 2 (Ott 2002) e Tron 2.0 (Ago 2003), ma e' possibile che anche altri lo siano. F.E.A.R e la sua patch 1.01 sono stati rilasciati in Ottobre 2005 e sono entrambi vulnerabili. ####################################################################### ====== 2) Bug ====== Il nuovo protocollo di rete usato dal Lithtech engine e' composto da un loop utilizzato per gestire tutti i pacchetti UDP ricevuti. Una funzione select() con un timeout di 30 secondi ricerca nuovi dati nella coda del socket. Se vengono ricevuti dati o il socket va' in timeout, viene chiamata la funzione recvfrom() ed il suo valore di ritorno viene controllare per sapere se c'e' stato un errore. In caso cio' sia avvenuto, il gioco chiama WSAGetLastError() per catturare il codice di errore e ritorna raggiungendo un controllo che viene fatto sempre prima di chiamare la solita select(). Questo "controllo principale" verifica soltanto che il codice di errore riportato da WSAGetLastError() (ed immagazzinato in una variabile specifica) sia un "Operation would block" (10035, e' l'unico tipo di errore accettato per continuare il loop di gestione dei pacchetti). Se un attacker invia un pacchetto UDP di zero bytes, la funzione recvfrom() ritorna tale quantita' di dati ricevuti ed un'istruzione controlla proprio se essa e' uguale a zero. In questo caso il flusso di codice ritorna al "controllo principale" che verifica il codice di errore (non settato e quindi uguale a zero) che non essendo 10035 fa' si che il loop termini. Dopo cio', il server non sara' piu' in grado di ricevere pacchetti in quanto il loop e' completamente morto. Un problema simile avviene se un attacker invia un pacchetto UDP maggiore/uguale a 8193 bytes e minore/uguale di 12280 (altrimenti la select() non lo cattura). Il "controllo principale" fallira' come prima in quanto il codice di errore sara' differente da 10035 (infatti sara' 10040, "Message too long"). ####################################################################### =========== 3) The Code =========== http://aluigi.org/poc/lithsock.zip ####################################################################### ====== 4) Fix ====== No fix. Proprio qualche settimana fa' rilasciai un altro advisory riguardo un altro bug nel Lithtech engine ignorato dagli sviluppatori, quindi e' inutile provare a ricontattarli. Comunque ho trovato un work-around che evita il problema ed ho quindi scritto un autopatcher universale che puo' correggere sia lithtech.exe (il server lanciato dal gioco) e server.dll (il server dedicato): http://aluigi.org/patches/lithsockfix.lpatch Update: Sembra che finalmente F.E.A.R. e' stato patchato (finalmente) con la versione 1.02 #######################################################################