ROP - Return Oriented Programing
Last updated
Last updated
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Programowanie Zorientowane na Zwracanie (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ż obecne w binarnym pliku lub załadowanych bibliotekach, znane 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, atakujący może skonstruować ładunek do wykonywania dowolnych operacji, skutecznie omijając zabezpieczenia NX/DEP.
Przechwytywanie przepływu kontrolnego: Najpierw atakujący musi przechwycić przepływ kontrolny programu, zazwyczaj wykorzystując przepełnienie bufora do nadpisania zapisanej adresu powrotu na stosie.
Łączenie gadżetów: Następnie atakujący starannie wybiera i łączy gadżety, aby wykonać pożądane działania. Może to obejmować przygotowanie argumentów do wywołania funkcji, wywołanie funkcji (np. system("/bin/sh")
) oraz obsługę wszelkich niezbędnych czynności porządkowych lub dodatkowych operacji.
Wykonanie ładunku: Gdy wrażliwa funkcja zwraca, zamiast wracać do legalnej 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ą umieszczane na stosie w odwrotnej kolejności (od prawej do lewej). Argumenty są umieszczane na stosie od prawej do lewej.
stdcall: Podobnie jak cdecl, ale wywoływana funkcja jest odpowiedzialna za czyszczenie stosu.
Najpierw załóżmy, że zidentyfikowaliśmy niezbędne gadżety w binarnym pliku lub jego załadowanych bibliotekach. Gadżety, którymi jesteśmy zainteresowani, to:
pop eax; ret
: Ten gadżet przenosi górną wartość stosu do rejestru EAX
, a następnie zwraca, co pozwala nam kontrolować EAX
.
pop ebx; ret
: Podobnie jak powyżej, ale dla rejestru EBX
, umożliwiając kontrolę nad EBX
.
mov [ebx], eax; ret
: Przenosi wartość w EAX
do lokalizacji pamięci wskazywanej przez EBX
, a następnie zwraca. Często nazywane jest to gadżetem write-what-where.
Dodatkowo mamy adres funkcji system()
dostępny.
Używając 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 celach wyrównania (opcjonalne)
Adresu funkcji system
(zakładając, że ASLR jest wyłączone i znana jest libc, więcej informacji w Ret2lib)
Miejsca na adres powrotu z system()
Adresu łańcucha "/bin/sh"
(parametr dla funkcji system)
Używa konwencji wywołań System V AMD64 ABI w systemach podobnych do Uniksa, gdzie pierwsze sześć argumentów całkowitych lub wskaźnikowych 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łań Windows x64 używa RCX
, RDX
, R8
i R9
dla pierwszych czterech argumentów całkowitych lub wskaźnikowych, a dodatkowe argumenty są przekazywane na stosie. Wartość zwracana jest umieszczana w RAX
.
Rejestry: Rejestry 64-bitowe obejmują RAX
, RBX
, RCX
, RDX
, RSI
, RDI
, RBP
, RSP
oraz R8
do R15
.
Dla naszych celów skupmy się na gadgetach, które pozwolą nam ustawić rejestr RDI (aby przekazać ciąg "/bin/sh" jako argument do system()) i następnie wywołać funkcję system(). Załóżmy, że zidentyfikowaliśmy następujące gadgety:
pop rdi; ret: Zdejmuje górną wartość ze stosu do RDI i następnie zwraca. Niezbędne do ustawienia naszego 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 użycia pwntools do skonfigurowania i wykonania łańcucha ROP mającego na celu wykonanie system('/bin/sh') na x64:
W tym przykładzie:
Wykorzystujemy gadżet pop rdi; ret
, aby ustawić 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 docelowe środowisko tego wymaga, co jest bardziej powszechne w x64, aby zapewnić prawidłowe wyrównanie stosu przed wywołaniem funkcji.
ABI x86-64 zapewnia, że stos jest wyrównany do 16 bajtów, gdy wykonywana jest instrukcja call. LIBC, aby zoptymalizować wydajność, używa instrukcji SSE (takich jak movaps), które wymagają tego wyrównania. Jeśli stos nie jest prawidłowo wyrównany (co oznacza, że RSP nie jest wielokrotnością 16), wywołania funkcji takich jak system zakończą się niepowodzeniem w łańcuchu ROP. Aby to naprawić, wystarczy dodać gadżet ret przed wywołaniem system w swoim łańcuchu ROP.
Ponieważ x64 używa rejestrów dla pierwszych kilku argumentów, często wymaga mniej gadżetów niż x86 do prostych wywołań funkcji, ale znalezienie i połączenie odpowiednich gadżetów może być bardziej skomplikowane z powodu zwiększonej liczby rejestrów i większej przestrzeni adresowej. Zwiększona liczba rejestrów i większa przestrzeń adresowa w architekturze x64 stwarzają zarówno możliwości, jak i wyzwania dla rozwoju exploitów, szczególnie w kontekście Programowania Opartego na Powrocie (ROP).
Sprawdź następującą stronę w celu uzyskania tych informacji:
Introduction to ARM64v8Stack Canaries: W przypadku BOF, konieczne jest ominięcie przechowywanych kanarków stosu, 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 mająca na celu wykonanie 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 stylu system('/bin/sh')
.
Ret2Syscall: Użyj ROP, aby przygotować wywołanie do syscall, np. execve
, i wykonać dowolne polecenia.
EBP2Ret i Łączenie EBP: Pierwsza technika wykorzysta EBP zamiast EIP do kontrolowania przepływu, a druga jest podobna do Ret2lib, ale w tym przypadku przepływ jest kontrolowany głównie za pomocą adresów EBP (chociaż również konieczne jest kontrolowanie EIP).
64 bity, Pie i nx włączone, brak kanarka, nadpisanie RIP adresem vsyscall
w celu powrotu do następnego adresu na stosie, który będzie częściowym nadpisaniem adresu, aby uzyskać część funkcji, która wycieka flagę
arm64, brak ASLR, gadżet ROP do uczynienia stosu wykonywalnym i skoku do shellcode w stosie
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)