Stack bufferoverflyt - Stack buffer overflow

I programvare forekommer en stabelbufferoverløp eller stabelbufferoverskridelse når et program skriver til en minneadresse på programmets anropsstabel utenfor den tiltenkte datastrukturen, som vanligvis er en buffer med fast lengde . Stack buffer overflow bugs er forårsaket når et program skriver mer data til en buffer som ligger på bunken enn det som faktisk er tildelt for den bufferen. Dette resulterer nesten alltid i korrupsjon av tilstøtende data på bunken, og i tilfeller der overløpet ble utløst av en feil, vil det ofte føre til at programmet krasjer eller fungerer feil. Stack bufferoverløp er en type mer generell programmeringsfeil som kalles bufferoverløp (eller bufferoverløp). Overfylling av en buffer på bunken er mer sannsynlig å avspore programkjøring enn å overfylle en buffer på haugen fordi stabelen inneholder returadressene for alle aktive funksjonsanrop.

Et stabelbufferoverløp kan være forårsaket bevisst som en del av et angrep som kalles stack smashing . Hvis det berørte programmet kjører med spesielle rettigheter, eller godtar data fra ikke -klarerte nettverter (f.eks. En webserver ), er feilen et potensielt sikkerhetsproblem. Hvis stabelbufferen er fylt med data levert fra en bruker som ikke er klarert, kan brukeren ødelegge stabelen på en slik måte at den injiserer kjørbar kode i det kjørende programmet og tar kontroll over prosessen. Dette er en av de eldste og mer pålitelige metodene for angriperne for å få uautorisert tilgang til en datamaskin.

Å utnytte stabelbuffer flyter over

Den kanoniske metoden for å utnytte et stabelbasert bufferoverløp er å overskrive funksjonsreturadressen med en peker til angriperstyrte data (vanligvis på selve bunken). Dette er illustrert med strcpy()i følgende eksempel:

#include <string.h>

void foo(char *bar)
{
   char c[12];

   strcpy(c, bar);  // no bounds checking
}

int main(int argc, char **argv)
{
   foo(argv[1]);
   return 0;
}

Denne koden tar et argument fra kommandolinjen og kopierer det til en lokal stabelvariabel c. Dette fungerer fint for kommandolinjeargumenter som er mindre enn 12 tegn (som du kan se i figur B nedenfor). Eventuelle argumenter større enn 11 tegn vil resultere i korrupsjon av bunken. (Det maksimale antallet tegn som er trygt er ett mindre enn størrelsen på bufferen her, fordi i C-programmeringsspråket avsluttes strenger med et nullbyte-tegn. En tolv tegn lang inngang krever dermed tretten byte for å lagre, inputen fulgte av sentinel null byte. Nullbyten ender deretter opp med å overskrive et minnested som er en byte utover slutten av bufferen.)

Programmet stabler inn foo()med forskjellige innganger:

Image
A. - Før data kopieres.
Image
B. - "hei" er det første kommandolinjeargumentet.
Image
C. - "AAAAAAAAAAAAAAAAAAAA \ x08 \ x35 \ xC0 \ x80" er det første kommandolinjeargumentet.

Legg merke til i figur C ovenfor, når et argument større enn 11 byte leveres på kommandolinjen, foo()overskriver lokale stabeldata, den lagrede rammepekeren og viktigst av alt returadressen. Når foo()returnerer den, kommer den returadressen av stabelen og hopper til den adressen (dvs. begynner å utføre instruksjoner fra den adressen). Dermed har angriperen overskrevet returadressen med en peker til stabelbufferen char c[12], som nå inneholder data fra angriperen. I en faktisk stabelbufferoverflyting vil strengen med "A" i stedet være shellcode som er egnet for plattformen og ønsket funksjon. Hvis dette programmet hadde spesielle rettigheter (f.eks. SUID -biten satt til å kjøre som superbruker ), kan angriperen bruke denne sårbarheten til å få superbrukerrettigheter på den berørte maskinen.

Angriperen kan også endre interne variable verdier for å utnytte noen feil. Med dette eksemplet:

#include <string.h>
#include <stdio.h>

void foo(char *bar)
{
   float My_Float = 10.5; // Addr = 0x0023FF4C
   char  c[28];           // Addr = 0x0023FF30

   // Will print 10.500000
   printf("My Float value = %f\n", My_Float);

    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       Memory map:
       @ : c allocated memory
       # : My_Float allocated memory

           *c                      *My_Float
       0x0023FF30                  0x0023FF4C
           |                           |
           @@@@@@@@@@@@@@@@@@@@@@@@@@@@#####
      foo("my string is too long !!!!! XXXXX");

   memcpy will put 0x1010C042 (little endian) in My_Float value.
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

   memcpy(c, bar, strlen(bar));  // no bounds checking...

   // Will print 96.031372
   printf("My Float value = %f\n", My_Float);
}

int main(int argc, char **argv)
{
   foo("my string is too long !!!!! \x10\x10\xc0\x42");
   return 0;
}

Plattformrelaterte forskjeller

En rekke plattformer har subtile forskjeller i implementeringen av samtalestakken som kan påvirke måten en stack bufferoverløp utnytter vil fungere. Noen maskinarkitekturer lagrer returadressen på toppnivået til anropsstakken i et register. Dette betyr at en overskrevet returadresse ikke vil bli brukt før en senere avvikling av anropsstakken. Et annet eksempel på en maskinspesifikk detalj som kan påvirke valg av utnyttelsesteknikker er det faktum at de fleste RISC -stilmaskinarkitekturer ikke vil tillate ujustert tilgang til minne. Kombinert med en fast lengde for maskinens opkoder, kan denne maskinbegrensningen gjøre hoppet til ESP -teknikk nesten umulig å implementere (med det ene unntaket når programmet faktisk inneholder den usannsynlige koden for eksplisitt å hoppe til stabelregisteret).

Stabler som vokser opp

Innenfor temaet stack stack buffer overflows, er en ofte diskutert-men-sjelden sett arkitektur en der stacken vokser i motsatt retning. Denne endringen i arkitekturen blir ofte foreslått som en løsning på problemet med stablingsbufferoverløp fordi overløp av en stabelbuffer som oppstår i samme stabelramme ikke kan overskrive returpekeren. Ytterligere undersøkelser av denne påståtte beskyttelsen finner det i beste fall en naiv løsning. Eventuelt overløp som oppstår i en buffer fra en tidligere stabelramme, vil fortsatt overskrive en returpeker og tillate skadelig utnyttelse av feilen. For eksempel, i eksemplet ovenfor, vil returpekeren for fooikke bli overskrevet fordi overløpet faktisk skjer i stabelrammen for memcpy. Men fordi bufferen som renner over under samtalen for å memcpyligge i en tidligere stabelramme, vil returpekeren for memcpyha en numerisk høyere minneadresse enn bufferen. Dette betyr at i stedet for returpekeren for foooverskriving, vil returpekeren for memcpybli overskrevet. På det meste betyr dette at å vokse bunken i motsatt retning vil endre noen detaljer om hvordan bunkebufferoverløp kan utnyttes, men det vil ikke redusere antallet utnyttbare feil vesentlig.

Beskyttelsesordninger

Gjennom årene har det blitt utviklet en rekke kontrollflytintegritetsordninger for å hemme utnyttelse av ondsinnet stack-bufferoverløp. Disse kan vanligvis deles inn i tre kategorier:

  • Oppdag at det har oppstått et stabelbufferoverløp, og forhindre dermed omdirigering av instruksjonspekeren til ondsinnet kode.
  • Forhindre utførelse av ondsinnet kode fra bunken uten å oppdage stabelen bufferoverflod direkte.
  • Randomiser minneplassen slik at det å finne kjørbar kode blir upålitelig.

Stack kanarifugler

Stack -kanarifugler, oppkalt etter sin analogi til en kanari i en kullgruve , brukes til å oppdage et stabelbufferoverløp før utførelse av ondsinnet kode kan oppstå. Denne metoden fungerer ved å plassere et lite heltall, hvis verdi er tilfeldig valgt ved programstart, i minnet like før stabelreturpekeren. De fleste bufferoverløp overskriver minne fra lavere til høyere minneadresser, så for å overskrive returpekeren (og dermed ta kontroll over prosessen) må også kanariverdien overskrives. Denne verdien kontrolleres for å sikre at den ikke har endret seg før en rutine bruker returpekeren på bunken. Denne teknikken kan øke vanskeligheten med å utnytte et stabelbufferoverløp i stor grad fordi det tvinger angriperen til å få kontroll over instruksjonspekeren på noen ikke-tradisjonelle måter, for eksempel å ødelegge andre viktige variabler på stabelen.

Ingen kjørbar stabel

En annen tilnærming for å forhindre utnyttelse av stabelbufferoverløp er å håndheve en minnepolicy på stakkminneområdet som ikke tillater utførelse fra bunken ( W^X , "Write XOR Execute"). Dette betyr at for å utføre skallkoden fra stabelen må en angriper enten finne en måte å deaktivere utførelsesbeskyttelsen fra minnet, eller finne en måte å sette shellcode nyttelast i et ikke-beskyttet område i minnet. Denne metoden blir mer populær nå som maskinvarestøtte for ikke-utførende flagg er tilgjengelig i de fleste stasjonære prosessorer.

Selv om denne metoden definitivt gjør at den kanoniske tilnærmingen til å stable utnyttelse av bufferoverløp mislykkes, er den ikke uten problemer. For det første er det vanlig å finne måter å lagre shellcode på i ubeskyttede minneområder som haugen, og så lite behov for endring i måten å utnytte på.

Selv om dette ikke var slik, er det andre måter. Den mest fordømmende er den såkalte return to libc- metoden for opprettelse av skallkoder. I dette angrepet vil den ondsinnede nyttelasten ikke laste bunken med skjellkode, men med en skikkelig anropsstabel, slik at kjøringen blir vektert til en kjede med standard bibliotekanrop, vanligvis med den virkningen at minnet deaktiveres og at shellcode kjøres som normalt. Dette fungerer fordi utførelsen faktisk aldri vektorer til selve bunken.

En variant av retur til libc er returorientert programmering (ROP), som setter opp en serie returadresser, som hver utfører en liten rekke kirsebærplukkede maskininstruksjoner i den eksisterende programkoden eller systembibliotekene, sekvens som ender med en retur. Disse såkalte gadgets oppnår hver enkel registermanipulering eller lignende utførelse før de returnerer, og ved å snøre dem sammen oppnår angriperens ender. Det er til og med mulig å bruke "returløs" returorientert programmering ved å utnytte instruksjoner eller grupper av instruksjoner som oppfører seg omtrent som en returinstruksjon.

Randomisering

I stedet for å skille koden fra dataene, er en annen begrensningsteknikk å introdusere randomisering til minneområdet til det utførende programmet. Siden angriperen må bestemme hvor kjørbar kode som kan brukes, er det enten en kjørbar nyttelast (med en kjørbar stabel) eller en er konstruert ved bruk av kode gjenbruk, for eksempel i ret2libc eller returorientert programmering (ROP). Randomisering av minneoppsettet vil som et konsept forhindre at angriperen vet hvor noen kode er. Imidlertid vil implementeringer vanligvis ikke randomisere alt; Vanligvis lastes selve kjørbare filen på en fast adresse, og selv når ASLR (randomisering av adresseromslayout) er kombinert med en ikke -eksekverbar stabel, kan angriperen bruke denne faste delen av minnet. Derfor bør alle programmer kompileres med PIE (posisjonsuavhengige kjørbare filer) slik at selv denne regionen i minnet er randomisert. Randomiseringens entropi er forskjellig fra implementering til implementering, og en lav nok entropi kan i seg selv være et problem når det gjelder brutal tvinge minneområdet som er randomisert.

Viktige eksempler

  • Den Morris ormen i 1988 spredt delvis ved å utnytte en stabel bufferoverflyt ved Unix finger -serveren. [1]
  • The Slammer-ormen i 2003 spredning ved å utnytte en stabel buffer overflow i Microsoft 's SQL server. [2]
  • Den Blaster-ormen i 2003 spredning ved å utnytte en stabel buffer overflow i Microsoft DCOM tjeneste.
  • Den Vittig ormen i 2004 spredning ved å utnytte en stabel buffer overflow i Internet Security Systems BlackICE Desktop Agent. [3]
  • Det er et par eksempler på at Wii tillater at vilkårlig kode kjøres på et umodifisert system. "Twilight hack" som innebærer å gi et langt navn til hovedpersonens hest i The Legend of Zelda: Twilight Princess , og "Smash Stack" for Super Smash Bros. Brawl som innebærer bruk av et SD -kort for å laste en spesialforberedt fil i editor i spillet. Selv om begge kan brukes til å utføre vilkårlig kode, brukes sistnevnte ofte til å laste inn Brawl selv med modifikasjoner påført.

Se også

Referanser