Leaking libc address with ROP
Résumé rapide
Trouver le décalage de l'overflow
Trouver les gadgets
POP_RDI
,PUTS_PLT
etMAIN_PLT
Utiliser les gadgets précédents pour fuir l'adresse mémoire de puts ou d'une autre fonction libc et trouver la version de la libc (téléchargez-la)
Avec la bibliothèque, calculer le ROP et l'exploiter
Autres tutoriels et binaires pour s'entraîner
Ce tutoriel va exploiter le code/binaire proposé dans ce tutoriel : https://tasteofsecurity.com/security/ret2libc-unknown-libc/ Autres tutoriels utiles : https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html
Code
Nom de fichier : vuln.c
ROP - Modèle de fuite de LIBC
Je vais utiliser le code situé ici pour réaliser l'exploit. Téléchargez l'exploit et placez-le dans le même répertoire que le binaire vulnérable et fournissez les données nécessaires au script :
1- Recherche du décalage
Le modèle nécessite un décalage avant de continuer avec l'exploit. S'il n'est pas fourni, il exécutera le code nécessaire pour le trouver (par défaut OFFSET = ""
) :
Exécutez python template.py
une console GDB s'ouvrira avec le programme planté. À l'intérieur de cette console GDB, exécutez x/wx $rsp
pour obtenir les octets qui allaient écraser le RIP. Enfin, obtenez le décalage en utilisant une console python:
Après avoir trouvé le décalage (dans ce cas 40), changez la variable OFFSET à l'intérieur du modèle en utilisant cette valeur.
OFFSET = "A" * 40
Une autre façon serait d'utiliser: pattern create 1000
-- exécuter jusqu'à ret -- pattern search $rsp
depuis GEF.
2- Recherche de Gadgets
Maintenant, nous devons trouver des gadgets ROP à l'intérieur du binaire. Ces gadgets ROP seront utiles pour appeler puts
afin de trouver la libc utilisée, et plus tard pour lancer l'exploit final.
Le PUTS_PLT
est nécessaire pour appeler la fonction puts.
Le MAIN_PLT
est nécessaire pour appeler la fonction principale à nouveau après une interaction pour exploiter le débordement à nouveau (des tours d'exploitation infinis). Il est utilisé à la fin de chaque ROP pour rappeler le programme.
Le POP_RDI est nécessaire pour passer un paramètre à la fonction appelée.
À cette étape, vous n'avez pas besoin d'exécuter quoi que ce soit car tout sera trouvé par pwntools pendant l'exécution.
3- Recherche de la bibliothèque libc
Il est maintenant temps de trouver quelle version de la bibliothèque libc est utilisée. Pour ce faire, nous allons fuir l'adresse en mémoire de la fonction puts
puis nous allons rechercher dans quelle version de bibliothèque la version de puts se trouve à cette adresse.
Pour ce faire, la ligne la plus importante du code exécuté est :
Cela enverra quelques octets jusqu'à ce que RIP soit écrasé : OFFSET
.
Ensuite, il définira l'adresse du gadget POP_RDI
afin que l'adresse suivante (FUNC_GOT
) soit enregistrée dans le registre RDI. Cela est nécessaire car nous voulons appeler puts en lui passant l'adresse de PUTS_GOT
car l'adresse en mémoire de la fonction puts est enregistrée à l'adresse pointée par PUTS_GOT
.
Ensuite, PUTS_PLT
sera appelé (avec PUTS_GOT
dans le registre RDI) afin que puts lise le contenu à l'intérieur de PUTS_GOT
(l'adresse de la fonction puts en mémoire) et l'affiche.
Enfin, la fonction principale est appelée à nouveau pour que nous puissions exploiter à nouveau le dépassement de tampon.
De cette manière, nous avons trompé la fonction puts pour qu'elle affiche l'adresse en mémoire de la fonction puts (qui se trouve dans la bibliothèque libc). Maintenant que nous avons cette adresse, nous pouvons rechercher quelle version de libc est utilisée.
Comme nous exploitons un binaire local, il n'est pas nécessaire de déterminer quelle version de libc est utilisée (il suffit de trouver la bibliothèque dans /lib/x86_64-linux-gnu/libc.so.6
).
Cependant, dans le cas d'une exploitation à distance, je vais expliquer ici comment vous pouvez le trouver :
3.1- Recherche de la version de libc (1)
Vous pouvez rechercher quelle bibliothèque est utilisée sur la page web : https://libc.blukat.me/ Cela vous permettra également de télécharger la version de libc découverte
3.2- Recherche de la version de libc (2)
Vous pouvez également faire :
$ git clone https://github.com/niklasb/libc-database.git
$ cd libc-database
$ ./get
Cela prendra du temps, soyez patient. Pour que cela fonctionne, nous avons besoin de :
Nom du symbole libc :
puts
Adresse libc divulguée :
0x7ff629878690
Nous pouvons déterminer quelle libc est probablement utilisée.
Nous obtenons 2 correspondances (vous devriez essayer la deuxième si la première ne fonctionne pas). Téléchargez la première :
Copiez la libc depuis libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so
vers notre répertoire de travail.
3.3- Autres fonctions pour la fuite
4- Recherche de l'adresse basée sur la libc et exploitation
À ce stade, nous devrions connaître la bibliothèque libc utilisée. Comme nous exploitons un binaire local, je vais utiliser simplement : /lib/x86_64-linux-gnu/libc.so.6
Ainsi, au début de template.py
, changez la variable libc en : libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Définir le chemin de la bibliothèque une fois connu
En donnant le chemin de la bibliothèque libc, le reste de l'exploit va être calculé automatiquement.
À l'intérieur de la fonction get_addr
, l'adresse de base de la libc va être calculée :
Notez que l'adresse de base finale de libc doit se terminer par 00. Si ce n'est pas votre cas, vous pourriez avoir divulgué une bibliothèque incorrecte.
Ensuite, l'adresse de la fonction system
et l'adresse de la chaîne "/bin/sh" vont être calculées à partir de l'adresse de base de libc et de la bibliothèque libc donnée.
Enfin, l'exploit d'exécution /bin/sh va être préparé et envoyé :
Expliquons ce dernier ROP.
Le dernier ROP (rop1
) s'est terminé en appelant à nouveau la fonction principale, puis nous pouvons exploiter à nouveau le débordement (c'est pourquoi l'OFFSET
est à nouveau présent). Ensuite, nous voulons appeler POP_RDI
pointant vers l'adresse de "/bin/sh" (BINSH
) et appeler la fonction system (SYSTEM
) car l'adresse de "/bin/sh" sera passée en paramètre.
Enfin, l'adresse de la fonction exit est appelée pour que le processus se termine proprement et qu'aucune alerte ne soit générée.
Ainsi, l'exploit exécutera un shell _/bin/sh_**.
4(2)- Utilisation de ONE_GADGET
Vous pourriez également utiliser ONE_GADGET pour obtenir un shell au lieu d'utiliser system et "/bin/sh". ONE_GADGET trouvera à l'intérieur de la bibliothèque libc un moyen d'obtenir un shell en utilisant juste une adresse ROP.
Cependant, il y a généralement certaines contraintes, les plus courantes et faciles à éviter sont comme [rsp+0x30] == NULL
. Comme vous contrôlez les valeurs à l'intérieur du RSP, vous n'avez qu'à envoyer quelques valeurs NULL supplémentaires pour éviter la contrainte.
FICHIER D'EXPLOIT
Vous pouvez trouver un modèle pour exploiter cette vulnérabilité ici :
Problèmes courants
MAIN_PLT = elf.symbols['main'] introuvable
Si le symbole "main" n'existe pas. Vous pouvez alors trouver où se trouve le code principal :
et définir manuellement l'adresse :
Puts non trouvé
Si le binaire n'utilise pas Puts, vous devriez vérifier s'il utilise
sh: 1: %s%s%s%s%s%s%s%s: not found
sh: 1: %s%s%s%s%s%s%s%s: not found
Si vous trouvez cette erreur après avoir créé tous les exploits : sh: 1: %s%s%s%s%s%s%s%s: not found
Essayez de soustraire 64 octets à l'adresse de "/bin/sh":
Last updated