Návratově orientované programování ( ROP ) je metoda zneužívání zranitelností v softwaru , pomocí které může útočník spustit kód, který potřebuje, pokud v systému existují ochranné technologie, například technologie, která zakazuje spouštění kódu z určitých paměťových stránek [ 1] . Metoda spočívá v tom, že útočník může získat kontrolu nad zásobníkem volání , najít v kódu sekvence instrukcí , které provádějí potřebné akce a nazývají se „gadgety“, provádět „gadgety“ v požadovaném pořadí [2] . "Gadget" obvykle končí návratovou instrukcí a je umístěn v hlavní paměti v existujícím kódu (v kódu programu nebo kódu sdílené knihovny ). Útočník dosáhne sekvenčního spouštění miniaplikací pomocí návratových instrukcí, uspořádá posloupnost miniaplikací tak, aby provedl požadované operace. Útok je proveditelný i na systémech, které mají mechanismy pro zabránění jednodušším útokům.
Return Oriented Programming je pokročilá verze útoku přetečením vyrovnávací paměti . V tomto útoku útočník používá chybu v programu, když funkce nekontroluje (nebo kontroluje nesprávně) hranice při zápisu do vyrovnávací paměti dat přijatých od uživatele. Pokud uživatel odešle více dat, než je velikost vyrovnávací paměti, dostanou se data navíc do oblasti paměti určené pro jiné lokální proměnné a mohou také přepsat návratovou adresu. Pokud je zpáteční adresa přepsána, pak při návratu funkce bude řízení přeneseno na nově zapsanou adresu.
V nejjednodušší verzi útoku přetečením vyrovnávací paměti útočník vloží kód ("payload") do zásobníku a poté přepíše návratovou adresu adresou instrukcí, které právě napsal. Až do konce 90. let většina operačních systémů neposkytovala žádnou ochranu proti těmto útokům. Systémy Windows neměly ochranu proti útokům přetečením vyrovnávací paměti až do roku 2004. [3] Operační systémy se nakonec začaly zabývat zneužíváním zranitelností přetečení vyrovnávací paměti tím, že označily určité stránky paměti jako nespustitelné (technika zvaná „Data Execution Prevention“). Je-li povolena prevence spouštění dat, zařízení odmítne spustit kód na stránkách paměti označených „pouze data“, včetně stránek obsahujících zásobník. To vám zabrání nasunout užitečné zatížení na zásobník a poté na něj skočit a přepsat zpáteční adresu. Později se zdálo , že hardwarová podpora pro prevenci spouštění dat zvyšuje ochranu.
Data Execution Prevention zabraňuje útoku výše popsanou metodou. Útočník je omezen na kód již v napadeném programu a sdílených knihovnách. Sdílené knihovny, jako je libc , však často obsahují funkce pro provádění systémových volání a další užitečné funkce pro útočníka, což umožňuje použití těchto funkcí při útoku.
Knihovní návratový útok také využívá přetečení vyrovnávací paměti. Návratová adresa je přepsána vstupním bodem požadované knihovní funkce. Buňky nad návratovou adresou jsou také přepsány, aby se předaly parametry funkci nebo řetězily více volání. Tuto techniku poprvé představil Alexander Peslyak (známý jako Solar Designer) v roce 1997 [4] a od té doby byla rozšířena, aby umožňovala neomezený řetězec volání funkcí. [5]
S rozšířením 64bitového hardwaru a operačních systémů je stále obtížnější provádět útok na návrat knihovny: v konvencích volání používaných v 64bitových systémech jsou první parametry předány funkci nikoli na zásobníku, ale v registrů. To komplikuje přípravu parametrů, které se mají během útoku volat. Vývojáři sdílených knihoven navíc začali odstraňovat nebo omezovat „nebezpečné“ funkce, jako jsou obaly systémových volání, z knihoven.
Dalším kolem vývoje útoku bylo použití částí funkcí knihovny namísto celých funkcí. [6] Tato technika hledá části funkcí, které posouvají data ze zásobníku do registrů. Pečlivý výběr těchto částí umožňuje v registrech připravit potřebné parametry pro volání funkce podle nové konvence. Dále se útok provádí stejným způsobem jako návratový útok knihovny.
Programování orientované na návrat rozšiřuje přístup k vypůjčování kódu tím, že útočníkovi poskytuje Turingovu kompletní funkčnost, včetně smyček a větví . [7] Jinými slovy, návratově orientované programování poskytuje útočníkovi možnost provést jakoukoli operaci. Hovav Shaham publikoval popis metody v roce 2007 [8] a demonstroval ji na programu, který využívá standardní knihovnu C a obsahuje zranitelnost přetečením bufferu. Programování orientované na návrat je lepší než ostatní typy výše popsaných útoků jak ve výrazové síle, tak v odolnosti vůči obranným opatřením. Žádná z výše uvedených metod boje proti útokům, včetně odstraňování nebezpečných funkcí ze sdílených knihoven, není účinná proti programování orientovanému na návrat.
Na rozdíl od útoku návratu do knihovny, který využívá celé funkce, programování orientované na návrat používá malé sekvence instrukcí zakončené návratovou instrukcí, tzv. „gadgety“. Gadgety jsou například zakončení stávajících funkcí. Na některých platformách, zejména x86 , se však gadgety mohou vyskytovat „mezi řádky“, to znamená při dekódování ze středu existující instrukce. Například následující posloupnost pokynů: [8]
test edi , 7 ; f7 c7 07 00 00 00 setnz byte [ ebp-61 ] ; 0f 95 45 c3když dekódování začne o jeden bajt později, dá
mov dword [ edi ], 0 f000000h ; c7 07 00 00 00 0f xchg ebp , eax ; 95 inc ebp ; 45 ret ; c3Gadgety mohou být také v datech, z toho či onoho důvodu, umístěny v sekci kódu. Je to proto, že instrukční sada x86 je poměrně hustá, což znamená, že existuje velká šance, že libovolný proud bajtů bude interpretován jako proud skutečných instrukcí. Na druhou stranu, v architektuře MIPS jsou všechny instrukce dlouhé 4 bajty a lze provést pouze instrukce zarovnané na adresy, které jsou násobky 4 bajtů. Proto neexistuje způsob, jak získat novou sekvenci „čtením mezi řádky“.
Útok využívá zranitelnost přetečení vyrovnávací paměti. Návratová adresa z aktuální funkce je přepsána adresou prvního gadgetu. Následující pozice v zásobníku obsahují adresy dalších miniaplikací a data používaná miniaplikacemi.
Ve své původní verzi pro platformu x86 jsou gadgety řetězce sekvenčně uspořádaných instrukcí bez skoků, které končí instrukcí blízkého návratu. V rozšířených verzích útoku nemusí být řetězové instrukce nutně sekvenční, ale jsou spojeny instrukcemi přímého skoku. Také roli finální instrukce může plnit další instrukce návratu (v x86 je také instrukce pro vzdálený návrat, instrukce pro blízký a vzdálený návrat s vymazáním zásobníku), instrukce nepřímého skoku nebo dokonce instrukce nepřímého volání. To komplikuje boj proti tomuto způsobu útoku.
Existují nástroje pro automatické vyhledání gadgetů a návrh útoku. Příkladem takového nástroje je ROPgadget. [9]
Existuje několik metod ochrany proti programování orientovanému na návrat. [10] Většina spoléhá na umístění programového kódu a knihoven na relativně libovolné adrese, takže útočník nemůže přesně předpovědět umístění instrukcí, které by mohly být užitečné v gadgetech, a proto nemůže vytvořit řetězec gadgetů k útoku. Jedna implementace této metody, ASLR , načítá sdílené knihovny na jiné adrese pokaždé, když je program spuštěn. Přestože je tato technologie široce používána v moderních operačních systémech, je zranitelná vůči útokům na únik informací a dalším útokům, které umožňují určit pozici známé funkce knihovny. Pokud útočník dokáže určit umístění jedné funkce, může určit umístění všech instrukcí v knihovně a provést návratově orientovaný programovací útok.
Přeskupovat můžete nejen celé knihovny, ale i jednotlivé instrukce programů a knihoven. [11] To však vyžaduje rozsáhlou podporu za běhu, jako je dynamický překlad, aby se permutované instrukce vrátily do správného pořadí pro provedení. Tato metoda ztěžuje hledání a používání gadgetů, ale má vysokou režii.
Přístup kBouncer [12] spočívá v kontrole, že instrukce návratu přenáší řízení na instrukci bezprostředně po instrukci volání. To značně snižuje množinu možných gadgetů, ale také způsobuje výrazný zásah do výkonu. [12] V rozšířené verzi návratově orientovaného programování lze navíc miniaplikace propojit nejen s návratovou instrukcí, ale také s nepřímým skokem nebo instrukcí volání. Proti takto rozšířenému útoku bude kBouncer neúčinný.