Returorienteret programmering ( ROP ) er en metode til at udnytte sårbarheder i software , hvorved en angriber kan udføre den kode, han har brug for, hvis der er beskyttende teknologier i systemet, for eksempel en teknologi, der forbyder udførelse af kode fra visse hukommelsessider [ 1] . Metoden ligger i, at angriberen kan få kontrol over opkaldsstakken , finde i koden sekvenser af instruktioner , der udfører de nødvendige handlinger og kaldes "gadgets", udføre "gadgets" i den ønskede rækkefølge [2]. En "gadget" slutter normalt med en returinstruktion og ligger i hovedhukommelsen i eksisterende kode (i programkode eller delt bibliotekskode ). Angriberen opnår sekventiel eksekvering af gadgets ved hjælp af returinstruktioner, arrangerer en sekvens af gadgets på en sådan måde, at den udfører de ønskede operationer. Angrebet er muligt selv på systemer, der har mekanismer til at forhindre enklere angreb.
Returorienteret programmering er en avanceret version af bufferoverløbsangrebet . I dette angreb bruger angriberen en fejl i programmet, når funktionen ikke tjekker (eller tjekker forkert) grænser, når han skriver til bufferen med data modtaget fra brugeren. Hvis brugeren sender flere data end bufferstørrelsen, kommer de ekstra data ind i hukommelsesområdet beregnet til andre lokale variabler og kan også overskrive returadressen. Hvis returadressen overskrives, vil kontrollen blive overført til den nyskrevne adresse, når funktionen vender tilbage.
I den enkleste version af et bufferoverløbsangreb skubber angriberen kode ("payload") ind på stakken og overskriver derefter returadressen med adressen på de instruktioner, de lige har skrevet. Indtil slutningen af 1990'erne gav de fleste operativsystemer ingen beskyttelse mod disse angreb. Windows- systemer havde ikke beskyttelse mod bufferoverløbsangreb før 2004. [3] Til sidst begyndte operativsystemer at håndtere udnyttelse af bufferoverløbssårbarheder ved at markere visse hukommelsessider som ikke-eksekverbare (en teknik kaldet "Data Execution Prevention"). Med forebyggelse af dataeksekvering aktiveret, vil maskinen nægte at udføre kode på hukommelsessider mærket "kun data", inklusive sider, der indeholder en stak. Dette forhindrer dig i at skubbe nyttelasten op på stakken og derefter hoppe til den og overskrive returadressen. Senere så det ud til, at hardwareunderstøttelse af Data Execution Prevention forbedrede beskyttelsen .
Forebyggelse af dataeksekvering forhindrer angrebet med metoden beskrevet ovenfor. Angriberen er begrænset til den kode, der allerede findes i det angrebne program og delte biblioteker. Dog indeholder delte biblioteker, såsom libc , ofte funktioner til at foretage systemkald og andre nyttige funktioner for en angriber, som gør det muligt at bruge disse funktioner i et angreb.
Biblioteksreturangrebet udnytter også et bufferoverløb. Returadressen overskrives af indgangspunktet for den ønskede biblioteksfunktion. Cellerne over returadressen overskrives også for at sende parametre til funktionen eller kæde flere opkald. Denne teknik blev først introduceret af Alexander Peslyak (kendt som Solar Designer) i 1997, [4] og er siden blevet udvidet til at tillade en ubegrænset kæde af funktionskald. [5]
Med udbredelsen af 64-bit hardware og operativsystemer er det blevet sværere at udføre et biblioteksreturangreb: i de kaldekonventioner, der bruges i 64-bit systemer, sendes de første parametre til funktionen, ikke på stakken, men i registre. Dette komplicerer forberedelsen af parametre, der skal kaldes under angrebet. Derudover begyndte udviklere af delte biblioteker at fjerne eller begrænse "farlige" funktioner, såsom systemopkaldsindpakninger, fra biblioteker.
Den næste udviklingsrunde af angrebet var brugen af dele af biblioteksfunktioner i stedet for hele funktioner. [6] Denne teknik leder efter dele af funktioner, der skubber data fra stakken til registre. Omhyggeligt valg af disse dele giver dig mulighed for at forberede de nødvendige parametre i registrene til at kalde funktionen i henhold til den nye konvention. Yderligere udføres angrebet på samme måde som biblioteksreturangrebet.
Returorienteret programmering udvider tilgangen til kodelån ved at give angriberen Turing-komplet funktionalitet, inklusive loops og branchs . [7] Med andre ord giver returorienteret programmering en angriber mulighed for at udføre enhver handling. Hovav Shaham udgav en beskrivelse af metoden i 2007 [8] og demonstrerede den på et program, der bruger standard C-biblioteket og indeholder en bufferoverløbssårbarhed. Returorienteret programmering er overlegen i forhold til de andre typer angreb beskrevet ovenfor i både udtrykskraft og modstand mod defensive foranstaltninger. Ingen af de ovennævnte metoder til at imødegå angreb, herunder fjernelse af farlige funktioner fra delte biblioteker, er effektive mod returorienteret programmering.
I modsætning til return-to-library-angrebet, som bruger hele funktioner, bruger return-orienteret programmering små sekvenser af instruktioner, der ender med en return-instruktion, de såkaldte "gadgets". Gadgets er for eksempel afslutninger på eksisterende funktioner. Men på nogle platforme, især x86 , kan gadgets forekomme "mellem linjerne", det vil sige, når de afkodes fra midten af en eksisterende instruktion. For eksempel følgende sekvens af instruktioner: [8]
test edi , 7 ; f7 c7 07 00 00 00 setnz byte [ ebp-61 ] ; 0f 95 45 c3når afkodningen starter én byte senere, giver
mov dword [ edi ], 0 f000000h ; c7 07 00 00 00 0f xchg ebp , eax ; 95 inc ebp ; 45 ret ; c3Gadgets kan også være i dataene, af en eller anden grund placeret i kodesektionen. Dette skyldes, at x86- instruktionssættet er ret tæt, hvilket betyder, at der er en stor chance for, at en vilkårlig strøm af bytes vil blive fortolket som en strøm af faktiske instruktioner. På den anden side, i MIPS-arkitekturen , er alle instruktioner 4 byte lange, og kun instruktioner justeret på adresser, der er multipla af 4 bytes, kan udføres. Derfor er der ingen måde at få en ny sekvens "ved at læse mellem linjerne".
Angrebet udnytter en bufferoverløbssårbarhed. Returadressen fra den aktuelle funktion overskrives med adressen på den første gadget. Efterfølgende positioner på stakken indeholder adresserne på de næste gadgets og de data, der bruges af gadgetsene.
I sin originale version til x86-platformen er gadgets kæder af sekventielt arrangerede instruktioner uden hop, der slutter med en næsten retur-instruktion. I udvidede versioner af angrebet er kædeinstruktioner ikke nødvendigvis sekventielle, men er forbundet med direkte springinstruktioner. Den endelige instruktions rolle kan også udføres af en anden retur-instruktion (i x86 er der også en fjern-retur-instruktion, nær- og fjern-retur-instruktioner med stakrydning), en indirekte hop-instruktion eller endda en indirekte opkaldsinstruktion. Dette komplicerer kampen mod denne angrebsmetode.
Der er værktøjer til automatisk at finde gadgets og designe et angreb. Et eksempel på et sådant værktøj er ROPgadget. [9]
Der er flere metoder til at beskytte mod returorienteret programmering. [10] De fleste er afhængige af placeringen af programkode og biblioteker på en relativt vilkårlig adresse, således at en angriber ikke nøjagtigt kan forudsige placeringen af instruktioner, der kan være nyttige i gadgets, og derfor ikke kan bygge en kæde af gadgets til at angribe. En implementering af denne metode, ASLR , indlæser delte biblioteker på en anden adresse, hver gang programmet køres. Men selvom denne teknologi er meget udbredt i moderne operativsystemer, er den sårbar over for informationslækageangreb og andre angreb, der gør det muligt at bestemme placeringen af en kendt biblioteksfunktion. Hvis en angriber kan bestemme placeringen af en funktion, kan han bestemme placeringen af alle instruktioner i biblioteket og udføre et returorienteret programmeringsangreb.
Du kan omarrangere ikke kun hele biblioteker, men også individuelle instruktioner til programmer og biblioteker. [11] Dette kræver dog omfattende run-time support, såsom dynamisk oversættelse, for at sætte de permuterede instruktioner tilbage i den korrekte rækkefølge for udførelse. Denne metode gør det sværere at finde og bruge gadgets, men det har store omkostninger.
kBouncer [12] tilgangen er at kontrollere, at returinstruktionen overfører kontrol til instruktionen umiddelbart efter opkaldsinstruktionen. Dette reducerer i høj grad antallet af mulige gadgets, men forårsager også et betydeligt præstationshit. [12] Derudover kan gadgets i en udvidet version af returorienteret programmering forbindes ikke kun med en returinstruktion, men også med en indirekte spring- eller opkaldsinstruktion. Mod et sådant udvidet angreb vil kBouncer være ineffektiv.