ROP - Return Oriented Programing
Last updated
Last updated
Naucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Naucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Return-Oriented Programming (ROP) to zaawansowana technika eksploatacji używana do obejścia środków bezpieczeństwa 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, efektywnie omijając zabezpieczenia NX/DEP.
Przechwycenie przepływu sterowania: Najpierw atakujący musi przejąć kontrolę nad przepływem programu, zazwyczaj wykorzystując przepełnienie bufora, aby nadpisać zapisany adres powrotu na stosie.
Ł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.
Wykonanie ładunku: Gdy podatna funkcja zwraca wartość, zamiast powrócić do prawidłowej lokalizacji, zaczyna wykonywać łańcuch gadżetów.
Zazwyczaj gadżety można znaleźć za pomocą ROPgadget, ropper lub bezpośrednio z pwntools (ROP).
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 wyczyszczenie stosu.
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ższy, 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()
.
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:
Instrukcji ret
w celu wyrównania (opcjonalnie)
Adres funkcji system
(przy założeniu wyłączonego ASLR i znanego libc, więcej informacji w Ret2lib)
Miejsce na adres powrotu z system()
Adres łańcucha "/bin/sh"
(parametr dla funkcji systemowej)
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 RAX
.
Konwencja wywoływania Windows x64 wykorzystuje 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 RAX
.
Rejestry: 64-bitowe rejestry obejmują RAX
, RBX
, RCX
, RDX
, RSI
, RDI
, RBP
, RSP
oraz R8
do R15
.
W naszym przypadku skupimy się na gadżetach, które pozwolą nam ustawić rejestr RDI (aby przekazać ciąg "/bin/sh" jako argument do funkcji system()) i następnie wywołać funkcję system(). Załóżmy, że zidentyfikowaliśmy następujące gadżety:
pop rdi; ret: Usuwa wartość z wierzchołka stosu do rejestru RDI i następnie zwraca. Istotne dla ustawienia argumentu dla system().
ret: Proste zwrócenie, przydatne do wyrównania stosu w niektórych scenariuszach.
I znamy adres funkcji system().
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:
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.
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.
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).
Sprawdź następującą stronę dla tych informacji:
Introduction to ARM64v8Canary na stosie: W przypadku przepełnienia bufora, konieczne jest ominięcie canary na stosie, 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.
Zauważ, że ROP to tylko technika służąca do wykonania dowolnego kodu. Na podstawie ROP opracowano wiele technik Ret2XXX:
Ret2lib: Użyj ROP, aby wywołać dowolne funkcje z załadowanej biblioteki z dowolnymi parametrami (zwykle coś w rodzaju system('/bin/sh')
.
Ret2Syscall: Użyj ROP do przygotowania wywołania syscall, np. execve
, i spraw, aby wykonywał dowolne polecenia.
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).
64 bity, Pie i nx włączone, brak canary, nadpisz RIP adresem vsyscall
w celu powrotu do następnego adresu na stosie, który będzie częściowym nadpisaniem adresu, aby uzyskać część funkcji wyciekającej flagę
arm64, brak ASLR, gadżet ROP do sprawienia, że stos jest wykonywalny i skok do shellcode na stosie
Dowiedz się i ćwicz Hacking w AWS:HackTricks Training AWS Red Team Expert (ARTE) Dowiedz się i ćwicz Hacking w GCP: HackTricks Training GCP Red Team Expert (GRTE)