Co to jest technika JOP (Jump-Oriented Programming)?

Przeczytało: 35, lip 5, 2025

Technika JOP (Jump-Oriented Programming) pozwala atakującemu na wykonanie złośliwych operacji poprzez łączenie krótkich fragmentów istniejącego w pamięci kodu zakończonych instrukcjami skoku. Dzięki wykorzystaniu gadgets opartych na JMP lub CALL omija ona zabezpieczenia oparte na wykrywaniu nadmiarowych RET oraz zakazy wykonywania kodu w obszarze danych. Ataki JOP często pojawiają się w eksploitach przeglądarek i złośliwym oprogramowaniu klasy APT, gdzie manipulacja vtable lub tablicami wskaźników umożliwia odnalezienie gadgets i zbudowanie łańcucha operacji. Obrona przed JOP wymaga wdrożenia weryfikacji poprawności przepływu sterowania (CFI), chronionych stosów powrotu (Shadow Stacks) oraz sprzętowych rozszerzeń Intel Control-flow Enforcement Technology (CET). Dodatkowe warstwy ochronne, jak dynamiczna randomizacja binarek i kompilatorowe transformacje instrukcji skoku, znacząco podnoszą barierę przed montowaniem efektywnych łańcuchów JOP.

Na czym polega Jump-Oriented Programming?

Jump-Oriented Programming to technika oparta na manipulacji przepływem sterowania w aplikacji bez wstrzykiwania własnego kodu. Zamiast klasycznych RET wykorzystywanych w ROP (Return-Oriented Programming), JOP czerpie garściami z instrukcji skoku – JMP, JMP [reg], CALL [reg] i podobnych. Atakujący lokalizuje w pamięci krótkie sekwencje instrukcji kończące się skokiem, czyli gadgets, a następnie składa je w łańcuch sterowany nadpisanymi wskaźnikami. Taki łańcuch pozwala na ustawianie wartości rejestrów, operacje arytmetyczne, warunki, a w efekcie wywołania systemowe (open, read, write, execve), przekładając się na pełną kontrolę nad procesem. Wykonanie każdego gadgeta przenosi sterowanie do kolejnego poprzez przekierowanie rejestra lub wskazania w pamięci, dzięki czemu atak obejmuje zarówno manipulację stosem, jak i strukturami typu vtable czy dispatch table.

Gadgets w JOP mogą być znacznie bardziej zróżnicowane niż w ROP. Poza prostymi "pop" i "mov" z reguły zawierają fragmenty pobierające z pamięci adres kolejnego skoku. Oznacza to, że atakujący może ukryć sekwencję adresów w strukturach obiektowych w języku C++ lub w tablicach wskaźników. Dzięki temu trudniej wykryć podejrzane nadpisania stogu, ponieważ łańcuch nie opiera się na typowym wzorcu RET.

Czym JOP różni się od ROP?

ROP wykorzystuje wyłącznie instrukcje RET do łączenia gadgets. Sprawia to, że systemy bezpieczeństwa wychwytujące nadmierne lub nietypowe sekwencje powrotów mogą je wykryć. JOP natomiast stosuje JMP i CALL w różnych formach – pośrednie (jmp [reg], call [reg]), pozwalając na przekierowanie sterowania na dowolny gadget, nawet gdy stos powrotu i tablice wskaźników pozostają nienaruszone. W efekcie zabezpieczenia bazujące na monitorowaniu stosu powrotu lub filtrowaniu RET tracą skuteczność.

Różnica techniczna między ROP a JOP widać także w sposobie przygotowania argumentów do wywołań systemowych. ROP wykorzystuje gadgety "pop rejestr; ret", aby kolejno ustawić argumenty w rejestrach, a następnie wywołać syscall. W JOP atakujący może użyć gadgetów z "mov [mem], reg" lub "load reg, [mem]", wykładając argumenty w strukturach danych i przekazując je następnie przez call [reg]. Takie podejście zwiększa elastyczność i pozwala stosować JOP tam, gdzie ROP okazuje się nieskuteczny.

Scenariusze wykorzystania JOP w praktyce

W atakach na przeglądarki internetowe często pojawia się JOP w kontekście silników JavaScript JIT. Atakujący wykorzystuje wyciek adresów bazowych kodu, a następnie generuje dynamicznie kod w pamięci wykonawczej zawierający gadgets. Nadpisanie dispatch table lub vtable klas w V8 czy Chakra pozwala przechwycić sterowanie i zbudować łańcuch działający w piaskownicy przeglądarki, prowadząc do ucieczki z VM (sandbox escape).

W złośliwym oprogramowaniu klasy APT JOP używa się do ominięcia zaawansowanych mechanizmów EDR. Malware ładuje biblioteki systemowe, wyszukuje gadgets w ntdll.dll czy kernel32.dll, a następnie wykorzystuje lukę use-after-free lub przepełnienie bufora do nadpisania wskaźników w strukturach obiektowych. Łańcuch JOP pozwala w efekcie na uruchomienie CreateProcessW, VirtualAlloc i WriteProcessMemory, skutkując załadowaniem i uruchomieniem dodatkowego payloadu.

Mechanizmy utrudniające ataki JOP

Control Flow Integrity (CFI)

CFI to metoda, która wprowadza monitorowanie poprawności wszystkich pośrednich wywołań i skoków. Kompilator zlicza autoryzowane cele skoków i osadza w kodzie etykiety, a w czasie wykonania każdy indirect call lub jmp jest sprawdzany pod kątem zgodności z tabelą legalnych adresów. Atak JOP, który próbuje skierować skok do nieautoryzowanego gadgetu, natychmiast zostaje wykryty i proces kończy działanie.

Shadow Stacks

Shadow Stacks to technika polegająca na utrzymaniu oddzielnego, chronionego stosu przeznaczonego wyłącznie do zapisywania wartości adresów powrotu. Przy każdym wywołaniu funkcji normalny stos dodaje adres powrotu, a Shadow Stack kopiuje go w osobnym obszarze pamięci niewidocznym dla aplikacji. W momencie wykonania instrukcji RET system porównuje wartość z Shadow Stack z tą na głównym stosie, a w razie rozbieżności przerywa działanie procesu. Chociaż JOP nie stosuje RET, chroniony Shadow Stack może być wykorzystywany także do weryfikacji poprawności skoków w strukturach wyrównanych do wywołań, utrudniajac przekierowanie sterowania.

Sprzętowe rozszerzenia Intel CET

Intel Control-flow Enforcement Technology (CET) łączy w sobie sprzętowy Shadow Stack oraz Indirect Branch Tracking (IBT). Shadow Stack jest realizowany przez dedykowane rejestry procesora i kontrolowany przez instrukcje ENDBRANCH. IBT wymaga, aby każde pośrednie wywołanie (jmp rax, call [rbx] itp.) było poprzedzone instrukcją ENDBRANCH32 lub ENDBRANCH64; w przypadku braku takiej instrukcji procesor zgłasza wyjątek. Dzięki CET JOP zostaje unieważnione w momencie skoku do kawałka kodu bez ENDBRANCH, skutecznie blokując gadgets.

Intel CET jest obsługiwane w procesorach od 10. generacji (Ice Lake, Tiger Lake) oraz w najnowszych wersjach systemów Windows i Linux, czyniąc je jednym z najsilniejszych obecnie dostępnych mechanizmów do ochrony przed przekierowaniem sterowania.

Uzupełniające techniki ochronne

Dynamiczna randomizacja binarek

Dynamiczna randomizacja binarek polega na kompilowaniu każdej instancji programu z losowym rozmieszczeniem sekcji kodu i bibliotek. Takie podejście skutecznie unieważnia narzędzia automatyzujące wyszukiwanie gadgets (ROPgadget, Ropper), ponieważ każde uruchomienie programu ma inny layout kodu.

Fine-Grained CFI

Fine-Grained CFI to rozszerzenie tradycyjnego CFI, które oprócz adresu docelowego skoku weryfikuje strukturę parametrów, rozmiar bloku danych i kontekst wywołania. Pozwala to odróżnić legalne wywołania generowane przez program od prób przekierowania sterowania przy użyciu nieodpowiednich struktur.

Kompilatorowe transformacje instrukcji skoku

Kompilatorowe transformacje instrukcji skoku to techniki, które zamieniają jmp i call na sekwencje z walidacją adresów. Na przykład każde call reg może zostać zastąpione przez instrukcje push context_id; jmp validate_and_call, gdzie validate_and_call sprawdza zgodność kontekstu i dopiero potem wykonuje pośredni skok.

Narzędzia pentesterskie i detekcja JOP

Analiza i wykrywanie JOP wymaga specjalistycznych narzędzi:

  • ROPGadget z opcją wyszukiwania gadgets kończących się jmp lub call.
  • Ropper pozwala na interaktywne eksplorowanie binarek pod kątem obu technik – ROP i JOP.
  • angr z dodatkiem angrop umożliwia automatyczne budowanie łańcuchów gadgetów w Pythonie.
  • EDR i systemy SIEM monitorujące wzorce pamięciowe i nietypowe przekierowania sterowania, wykrywają ciągi adresów odbiegające od profilu aplikacji.

Muśnięcie tematów takich jak JIT spraying czy vtable hijacking uzupełnia arsenał przeciwnika, więc obrona powinna łączyć statyczne techniki (CFI, Shadow Stacks, CET) z dynamicznym monitoringiem i analizą behawioralną.

Opracowano na podstawie:

  • wikipedia.org, Return-oriented programming, https://en.wikipedia.org/wiki/Return-oriented_programming, Data odczytu: 2025.05.10
  • arm.com, Jump-oriented programming, https://developer.arm.com/documentation/102433/0200/Jump-oriented-programming, Data odczytu: 2025.05.10
  • stackexchange.com, Concept of Jump-Oriented-Programming (JOP), https://security.stackexchange.com/questions/201196/concept-of-jump-oriented-programming-jop, Data publikacji: 2019.01.10, Data odczytu: 2025.05.10

Zostaw komentarz

Zaloguj się


Kategorie