####################################################################### Luigi Auriemma Applicazione: Half-Life (http://half-life.sierra.com) Versioni: 1.1.1.0 and previous versions (naturally all the MODs based on this game are vulnerable) Also the 3.1.1.1c1 and 4.1.1.1c1 version of the free dedicated servers are vulnerable! Piattaforme: Windows e Linux Bugs: Alcune funzioni del server non effettuano alcun controllo sulla lunghezza delle stringhe inviate dal client, quindi verranno sovrascritte zone di memoria necessarie alla corretta esecuzione del programma non confermata) e Denial of Service contro i SERVERS vulnerabili Date: 29 Jul 2003 Author: Luigi Auriemma e-mail: aluigi@autistici.org web: aluigi.org ####################################################################### 1) Introduzione 2) Riassunto dei bugs 3) Bugs details (long, unuseful and boring) [inglese] 4) Il Codice 5) Fix ####################################################################### =============== 1) Introduzione =============== Half-Life e' stato rilasciato nel 1998 e anche dopo tutti questi anni e' considerato a tutti gli effetti uno dei FPS piu' popolari. Ma piu' che il gioco in se', la vera fortuna e' stata data dalla comunita' di utenti che ha contribuito al suo successo con i suoi MODs per questo gioco e sicuramente il piu' famoso e giocato on-line e' CounterStrike. E' stato sviluppato da Valve (http://www.valvesoftware.com) e pubblicato da Sierra (http://www.sierra.com). ####################################################################### ===================== 2) Riassunto dei bugs ===================== Il bug che ho trovato e' un buffer-oveflow che interessa SOLO i servers di Half-Life. Entrambi il server dedicato ed il server del gioco sono vulnerabili. L'unica limitazione riscontrata in tale buffer-overflow e' l'impossibilita' di usare alcuni caratteri nello shellcode e cio' puo' limitare l'esecuzione di codice remoto. Inoltre esiste anche un Denial of Service che paralizza completamente il server (in pratica il server entra in un loop infinito se riceve una stringa troppo lunga in una determinata posizione nella richiesta inviata dal client). ####################################################################### ==================================================== 3) Bugs details (long, useless and boring) [inglese] ==================================================== --- The following information is long, read only if you want some useless details on this vulnerability or simply if you want too appreciate all the time I have lost debugging this game, it has not been completely lost 8-) --- The first thing that I want to specify is that some bytes cannot be used to fully exploit the following buffer-overflow, so the code execution could theoretically be limited. The explanation of the bug is divided into 4 sections used to show the effects of the long string as parameter and value on the graphical game and on the dedicated server: BUG 1: buffer-overflow BUG 2: freeze (infinite loop) With parameter and value I mean: \name\Test | | | value parameter The problem happens because Half-Life uses other instructions if the total lenght of the string sent by the client is major than 256 bytes: cmp ecx, edi jl 00d3e687 --------------------------- BUG 1: HLDS.EXE (parameter) --------------------------- First of all, I want to explain the buffer overflow that only occurs within the Half-Life dedicated server. During my explanation I will refer only to the exact addresses of the Half-Life 1.1.1.0 dedicated server on Windows (hlds.exe from the retail game). The problem happens when a too long parameter is passed in the packet to join multiplayer matches sent by the client to the server and the following is an example in C language of the UDP packet plus a big parameter (that I have called PAYLOAD): #define PAYLOAD [268 chars] #define BOF "\xff\xff\xff\xff" \ /* 1 */ "connect %d" \ /* 2 */ " %s \"" \ "\\prot\\2" \ "\\unique\\-1" \ "\\raw\\00000000000000000000000000000000" \ "\" \"" \ "\\model\\" MODEL \ "\\topcolor\\" TOPCOLOR \ "\\bottomcolor\\" BOTTOMCOLOR \ "\\rate\\9999.000000" \ "\\cl_updaterate\\20" \ "\\cl_lw\\1" \ "\\cl_lc\\1" \ "\\cl_dlmax\\128" \ "\\hud_classautokill\\1" \ "\\name\\" NAME \ /* 3 */ "\\" PAYLOAD "\\value" \ "\"\n" where: 1) the first "%d" is the protocol version supported by the server 2) "%s" is the challenge key sent by the server previously 3) PAYLOAD is a long string of 268 chars (268 are needed to overwrite the stored EBP and EIP registers in the stack, respectively at offset 260 and 264 of the PAYLOAD string) The dangerous code is located in the function at address 0xD3E3F0 of SWDS.DLL (address that in memory will become 0x63ce3f0, so if you want to debug it in real-time remember to add 0x5690000...). This function seems to be something similar to a strcpy() function but it is used ONLY with parameters, and it looks not only for NULL bytes but also for backslashes '\' in the parameters sent by the client. The problem however is located "exactly" in the loop that starts from address 0xD3E425 to 0xD3E432: :00D3E425 3C5C cmp al, 5C :00D3E427 740B je 00D3E434 :00D3E429 8801 mov byte ptr [ecx], al :00D3E42B 8A4601 mov al, byte ptr [esi+01] :00D3E42E 41 inc ecx :00D3E42F 46 inc esi :00D3E430 3AC3 cmp al, bl :00D3E432 75F1 jne 00D3E425 As you can see, this loop makes the following things: 1) if the current byte in our string is equal to '\' the loop will be broken 2) it stores the current byte in memory (buffer overflow) 3) it gets the next byte from our string 4) the pointer now points to the next memory position and next byte of the string 5) if the current byte in our string is a NULL byte the loop will be broken (BL in our case is a NULL byte) Wonderful! In the meantime no instruction checks if the string/parameter passed by the client is too long for the local buffer, so our Half-Life server is in a very bad situation... In fact the previously stored EIP (that was equal to 0x63ce614) has been fully overwritten by our string. ------------------------- BUG 1: HL.EXE (parameter) ------------------------- :01D3E425 3C5C cmp al, 5C :01D3E427 740B je 01D3E434 :01D3E429 8801 mov byte ptr [ecx], al :01D3E42B 8A4601 mov al, byte ptr [esi+01] :01D3E42E 41 inc ecx :01D3E42F 46 inc esi :01D3E430 3AC3 cmp al, bl :01D3E432 75F1 jne 01D3E425 (l'eseguibile sempre scompattarsi in memoria dove essere stato avviato o qualcosa di simile) ----------------------- BUG 2: HLDS.EXE (value) ----------------------- A similar problem happens in the value field, for example inserting the PAYLOAD as value of a normal parameter, like (C language): "\\parameter\\" PAYLOAD In the dedicated server (SWDS.DLL) after the vulnerable loop that checks the parameter (0xD3E425 as seen previously) there is another loop that instead checks the value of the parameters. This loop goes from 0xD3E45B to 0xD3E468: :00D3E45B 3C5C cmp al, 5C :00D3E45D 740B je 00D3E46A :00D3E45F 8801 mov byte ptr [ecx], al :00D3E461 8A4601 mov al, byte ptr [esi+01] :00D3E464 41 inc ecx :00D3E465 46 inc esi :00D3E466 3AC3 cmp al, bl :00D3E468 75F1 jne 00D3E45B This loop copies our string/value to another buffer in memory that is located before the buffer used to store the parameter. The stack of these functions is similar to the following: [value_buff]...[parameter_buff]...[EBP][EIP] 0x415df94 0x415e094 0x415e19c Fortunately Half-Life can accept only values minor/equal than 380 chars (parameters limit is minor than 380), so the string to use to exploit the server is limited and cannot reach the position in memory where is stored the EIP value. Practical resuming: - The function that checks the value uses a buffer that starts from the memory position: 0x415df94 - The function that checks the parameter uses a buffer that starts from the memory position: 0x415e094 - EIP is stored at position: 0x415e19c So: 0x415df94 + 0x17c = 0x415E110 (that is 140 bytes minor than the position of the stored EIP in memory) However the problem is not finished here because a buffer-overflow doesn't exist in the value, but a good Denial of Service does exist. In fact, the effect in the Half-Life dedicated server is an infinite loop in SWDS.DLL, from the memory address 0x63ce60d (0xD3E60D) to 0x63ce645 (0xD3E645). This function simply makes an infinite check of the same string given by the user, this is the simple cause of the DoS. --------------------- BUG 2: HL.EXE (value) --------------------- The problem is the same in HLDS.EXE The only things that change are the offsets of the checking function because the vulnerable loop is in HL.EXE and in memory it starts from 0x01d3e60d to 0x01d3e645: :01D3E60D 57 push edi :01D3E60E 56 push esi :01D3E60F E8DCFDFFFF call 01D3E3F0 <-- vulnerabile ... :01D3E643 84C0 test al, al :01D3E645 75C6 jnz 01D3E60D --- NOTE: dalla versione 4.1.1.1c e 3.1.1.1c del server dedicato, Valve ha provato a patchare il buffer-overflow... il risultato ora e' la scomparsa del BoF ma un bel freeze. Cosa e' peggio? ####################################################################### ============ 4) Il Codice ============ L'exploit e' molto semplice. E' una via di mezzo tra un DoS ed un exploit di esecutione di codice. Cio' che ho fatto e' stato soltanto sovrascrivere l'indirizzo di ritorno con l'indirizzo di una funzione di SWDS.DLL che mostra un messaggio nella console del server dedicato e poi crasha. Ho scelto questo metodo perche' non mi piacciono molto i buffer overflow e perche' questo metodo permette a chiunque abbia un server dedicato 1.1.1.0 di vedere il mio messaggio nella console. Comunque l'exploit puo' essere usato contro entrambi il server dedicato e non, sovrascrivendo l'indirizzo di ritorno con 0x063c27f5. Puo' essere compilato su Windows e *nix e puo' testare sia il buffer-oveflow nel parametro (possibile esecuzione di codice) e quello nel valore (paralisi del server): http://aluigi.org/poc/hlbof-server.zip ####################################################################### ====== 5) Fix ====== Valve e' stata contattata oltre 3 mesi fa' (a partire dal 14 Aprile 2003) e mi informo' che stavano lavorando per rilasciare una patch. Da allora ho contattato Valve diverse volte... ma fino ad ora non si e' ancora vista una patch. --- Ho trovato un piccolo metodo per aggirare il problema (si tratta solo di un JUMP). Praticamente evito che i dati inviati dal client siano gestiti dalla funzione vulnerabile se sono piu' grandi di 256 bytes. Non ho il codice sorgente del gioco e non sono neanche un programmatore di Valve quindi il mio e' soltanto un metodo personale che non ha dato nessun problema fino ad ora. Chiunque voglia provarlo in attesa della patch ufficiale di Valve mi faccia sapere se incontra problemi: Server dedicato di Half-Life 1.1.1.0 e 4.1.1.0 (Windows): http://aluigi.org/patches/hlbof-server-1110-fix.zip #######################################################################