Return oriented programmering ( ROP ) är en metod för att utnyttja sårbarheter i programvara , med vilken en angripare kan exekvera den kod han behöver om det finns skyddsteknologier i systemet, till exempel en teknik som förbjuder exekvering av kod från vissa minnessidor [ 1] . Metoden ligger i det faktum att angriparen kan få kontroll över anropsstacken , hitta i koden sekvenser av instruktioner som utför nödvändiga åtgärder och kallas "prylar", exekvera "prylar" i önskad sekvens [2]. En "gadget" slutar vanligtvis med en returinstruktion och finns i huvudminnet i befintlig kod (i programkod eller delad bibliotekskod ). Angriparen uppnår sekventiell exekvering av gadgets med hjälp av returinstruktioner, arrangerar en sekvens av gadgets på ett sådant sätt att de utför de önskade operationerna. Attacken är genomförbar även på system som har mekanismer för att förhindra enklare attacker.
Returorienterad programmering är en avancerad version av buffertspillattacken . I denna attack använder angriparen en bugg i programmet när funktionen inte kontrollerar (eller kontrollerar felaktigt) gränser när han skriver till bufferten av data som tas emot från användaren. Om användaren skickar mer data än buffertstorleken hamnar den extra datan i minnesområdet avsett för andra lokala variabler och kan även skriva över returadressen. Om returadressen skrivs över kommer kontrollen att överföras till den nyskrivna adressen när funktionen återkommer.
I den enklaste versionen av en buffertspillsattack trycker angriparen kod ("nyttolast") till stacken och skriver sedan över returadressen med adressen till instruktionerna de just skrev. Fram till slutet av 1990-talet gav de flesta operativsystem inget skydd mot dessa attacker. Windows -system hade inte skydd mot buffertspillattacker förrän 2004. [3] Så småningom började operativsystemen hantera exploatering av sårbarheter i buffertspill genom att markera vissa sidor i minnet som icke-körbara (en teknik som kallas "Data Execution Prevention"). Med förhindrande av dataexekvering aktiverat, kommer maskinen att vägra exekvera kod på minnessidor märkta "endast data", inklusive sidor som innehåller en stack. Detta hindrar dig från att trycka på nyttolasten på stapeln och sedan hoppa till den och skriva över returadressen. Senare verkade hårdvarustöd för Data Execution Prevention förbättra skyddet .
Data Execution Prevention förhindrar attacken med metoden som beskrivs ovan. Angriparen är begränsad till koden som redan finns i det attackerade programmet och delade biblioteken. Däremot innehåller delade bibliotek, som libc , ofta funktioner för att göra systemanrop och andra användbara funktioner för en angripare, vilket gör att dessa funktioner kan användas i en attack.
Bibliotekreturattacken utnyttjar också ett buffertspill. Returadressen skrivs över av ingångspunkten för den önskade biblioteksfunktionen. Cellerna ovanför returadressen skrivs också över för att skicka parametrar till funktionen eller kedja flera anrop. Denna teknik introducerades först av Alexander Peslyak (känd som Solar Designer) 1997, [4] och har sedan dess utökats för att tillåta en obegränsad kedja av funktionsanrop. [5]
Med spridningen av 64-bitars hårdvara och operativsystem har det blivit svårare att utföra en biblioteksreturattack: i de anropskonventioner som används i 64-bitarssystem skickas de första parametrarna till funktionen inte på stacken, utan i register. Detta komplicerar förberedelserna av parametrar som ska anropas under attacken. Dessutom började utvecklare av delade bibliotek ta bort eller begränsa "farliga" funktioner, såsom systemanropspaket, från biblioteken.
Nästa utvecklingsomgång av attacken var användningen av delar av biblioteksfunktioner istället för hela funktioner. [6] Denna teknik letar efter delar av funktioner som skickar data från stacken till register. Noggrant val av dessa delar gör att du kan förbereda de nödvändiga parametrarna i registren för att anropa funktionen enligt den nya konventionen. Vidare utförs attacken på samma sätt som bibliotekets returattack.
Returorienterad programmering utökar metoden för kodlån genom att förse angriparen med Turing-komplett funktionalitet, inklusive loopar och grenar . [7] Med andra ord, returorienterad programmering ger en angripare möjligheten att utföra vilken operation som helst. Hovav Shaham publicerade en beskrivning av metoden 2007 [8] och demonstrerade den i ett program som använder standard C-biblioteket och innehåller en sårbarhet för buffertspill. Returorienterad programmering är överlägsen de andra typerna av attacker som beskrivs ovan i både uttryckskraft och motstånd mot defensiva åtgärder. Ingen av ovanstående metoder för att motverka attacker, inklusive borttagning av farliga funktioner från delade bibliotek, är effektiv mot returorienterad programmering.
Till skillnad från retur-till-bibliotek-attacken, som använder hela funktioner, använder returorienterad programmering små sekvenser av instruktioner som slutar med en returinstruktion, de så kallade "prylarna". Prylar är till exempel avslutningar på befintliga funktioner. På vissa plattformar, särskilt x86 , kan dock prylar förekomma "mellan raderna", det vill säga vid avkodning från mitten av en befintlig instruktion. Till exempel följande sekvens av instruktioner: [8]
test edi , 7 ; f7 c7 07 00 00 00 setnz byte [ ebp-61 ] ; 0f 95 45 c3när avkodningen startar en byte senare, ger
mov dword [ edi ], 0 f000000h ; c7 07 00 00 00 0f xchg ebp , eax ; 95 inc ebp ; 45 ret ; c3Prylar kan också finnas i datan, av en eller annan anledning finns i koddelen. Detta beror på att x86- instruktionsuppsättningen är ganska tät, vilket betyder att det finns en stor chans att en godtycklig ström av byte kommer att tolkas som en ström av faktiska instruktioner. Å andra sidan, i MIPS-arkitekturen är alla instruktioner 4 byte långa, och endast instruktioner anpassade till adresser som är multiplar av 4 byte kan exekveras. Därför finns det inget sätt att få en ny sekvens "genom att läsa mellan raderna".
Attacken utnyttjar en sårbarhet för buffertspill. Returadressen från den aktuella funktionen skrivs över med adressen till den första gadgeten. Efterföljande positioner på stacken innehåller adresserna till nästa prylar och data som används av prylarna.
I sin ursprungliga version för x86-plattformen är prylar kedjor av sekventiellt arrangerade instruktioner utan hopp, som slutar med en nästan returinstruktion. I utökade versioner av attacken behöver kedjeinstruktioner inte nödvändigtvis vara sekventiella, utan är sammankopplade med direkta hoppinstruktioner. Den slutliga instruktionens roll kan också utföras av en annan returinstruktion (i x86 finns det också en fjärråtergångsinstruktion, nära- och fjärråtergångsinstruktioner med stackrensning), en indirekt hoppinstruktion eller till och med en indirekt anropsinstruktion. Detta komplicerar kampen mot denna attackmetod.
Det finns verktyg för att automatiskt hitta prylar och designa en attack. Ett exempel på ett sådant verktyg är ROPgadget. [9]
Det finns flera metoder för att skydda mot returorienterad programmering. [10] De flesta förlitar sig på platsen för programkod och bibliotek på en relativt godtycklig adress, så att en angripare inte kan förutsäga platsen för instruktioner som kan vara användbara i prylar och därför inte kan bygga en kedja av prylar för att attackera. En implementering av denna metod, ASLR , laddar delade bibliotek på en annan adress varje gång programmet körs. Men även om denna teknik används flitigt i moderna operativsystem, är den sårbar för attacker med informationsläckage och andra attacker som gör det möjligt att bestämma positionen för en känd biblioteksfunktion. Om en angripare kan bestämma platsen för en funktion kan han bestämma platsen för alla instruktioner i biblioteket och utföra en returorienterad programmeringsattack.
Du kan ordna om inte bara hela bibliotek, utan också individuella instruktioner för program och bibliotek. [11] Detta kräver dock omfattande körtidsstöd, såsom dynamisk översättning, för att återställa de permuterade instruktionerna i rätt ordning för exekvering. Denna metod gör det svårare att hitta och använda prylar, men har en hög omkostnad.
Tillvägagångssättet kBouncer [12] är att kontrollera att returinstruktionen överför kontrollen till instruktionen omedelbart efter anropsinstruktionen. Detta minskar mängden möjliga prylar avsevärt, men orsakar också en betydande prestandaträff. [12] Dessutom, i en utökad version av returorienterad programmering, kan prylar kopplas inte bara med en returinstruktion, utan också med en indirekt hopp- eller anropsinstruktion. Mot en sådan utökad attack kommer kBouncer att vara ineffektiv.