Buffertöverskridning eller buffertspill (engelska buffer overflow) är en dataterm för händelsen när en process (ofta ett datorprogram) försöker spara mer information (data) i en buffert än vad som får plats (här kan en "buffert" vara vilket som helst utrymme reserverat för data). Ofta skrivs då efterföljande datastrukturer över, vilket kan leda till störningar, krasch eller säkerhetshål, beroende på vilka strukturer som skrivs över, med vad de skrivs över och huruvida situationen hanteras av programvaran.

Överskrivning av returadress redigera

Det är vanligt att datorprogram lagrar buffertar på stacken, en datastruktur för tillfällig data, där också bland annat returadressen för programmets subrutiner eller funktioner lagras. På grund av det sätt på vilken data lagras kan datan som var avsett för bufferten komma att skriva över funktionens returadress. Den som konstruerar data för en buffertattack kan alltså kontrollera på vilken minnesadress datorn hämtar fortsatta instruktioner efter att funktionen som är föremål för attacken arbetat färdigt. I det enklaste fallet väljer han en adress som pekar på den data han själv konstruerat och kan alltså få programmet att utföra godtyckliga maskinkodsinstruktioner (se exemplet nedan). Datan som erbjuds kan behöva utformas på speciella sätt för att programmet inte skall krascha på grund av ogiltigt data innan kontrollen överförs till den konstruerade datan.

För att värja sig mot denna attack kan man förbjuda datorn att köra maskinkod i det minnesområde där bufferten finns. Detta gör attacken svårare, men oftast finns det i programmet eller i de programbibliotek programmet laddat någon kod som kan manipuleras att köra datan som ett program. Som returadress anges då inte adressen till programmet på stacken utan adressen till en biblioteksfunktion i minnet. Om funktionen kör ett externt program och som parametrar har programmets namn och indata för programmet (såsom execve) så gäller det helt enkelt att som parametrar ange en kommandotolk och kommandon för kommandotolken. Mer komplicerade scenarion är vanliga och kan kräva god kunskap om datoromgivningen.

I en del operativsystem kan man låta systemet slumpvis välja var i minnet program, programbibliotek och datastrukturer placeras. Det är då svårare att konstruera datan så att den tolkas som meningsfulla programinstruktioner.

Buffertöverskridningar i olika programspråk redigera

Speciellt utsatta för buffertöverskridning är oförsiktigt skrivna program i vissa kompilerade högnivåspråk som C eller C++. I dessa språk görs inga försök att förebygga ogiltiga buffertaccesser, dels av prestandaskäl, dels på grund av dåligt formgivna biblioteksanrop (se till exempel sprintf, som har varit en stor källa av datorintrång på grund av buffertöverskridningar). Kompilerade programspråk som Java eller C#, och samtliga skriptspråk, har automatisk kontroll av ogiltiga buffertaccesser. Detta gör att datorprogram skrivna i dessa språk kan vara något långsammare än motsvarande program i C, men den ökade säkerheten överväger i många fall den eventuella prestandaminskningen.

Teknisk beskrivning redigera

En buffertöverskridning inträffar när data skrivs till en buffert och, på grund av otillräcklig kontroll över storleken på buffert kontra storleken på datan, information skrivs till dataadresserna bredvid den allokerade bufferten. Detta inträffar oftast när strängar kopieras från en buffert till en annan.

Vid sidan av överskrivning av returadresser på stacken, beskriven ovan, kan attacker konstrueras också för annat data som följer den överskridna bufferten. Beroende på implementationen av det använda programspråket kan till exempel pekare till funktioner lagras bland variabler och denna pekare användas på motsvarande sätt som returadressen. Detaljerna i attacken är dock annorlunda.

Enkelt exempel redigera

I följande exempel har ett program definierat upp två data-objekt vilka ligger bredvid varandra i minnet; en åtta byte lång sträng-buffert, A, och ett två byte långt heltal, B. Inledningsvis innehåller A endast noll-värden och B innehåller siffran 3. Varje tecken i strängbufferten tar upp en byte.

A A A A A A A A B B
0 0 0 0 0 0 0 0 0 3

Nu försöker programmet lagra strängen "excessive" i sträng-bufferten A, med ett avslutande 0-byte-tecken för att markera slutet på strängen (detta enligt konventionerna i programspråken C eller C++). I och med att vi inte kontrollerar längden mot strängen och om den får plats i bufferten A så skriver vi nu över värdet av B:

A A A A A A A A B B
'e' 'x' 'c' 'e' 's' 's' 'i' 'v' 'e' 0

Även om programmeraren inte förväntade sig att B:s värde skulle ändra på sig har B:s värde nu ersatts av ett värde från en del av den sträng vi försökte skriva in i A. I detta exemplet, på ett system med en big endian-byteordning som använder ASCII skulle de inskrivna tecknen, "e" och en efterföljande 0-byte-tecken, motsvara siffran 25856. Om A och B var de enda objekten definierade så skulle en ännu längre sträng som gick förbi även B:s gräns antagligen leda till ett fel, tex ett segmenteringsfel, som skulle avsluta processen.

Externa länkar redigera

Källor redigera

Den här artikeln är helt eller delvis baserad på material från engelskspråkiga Wikipedia.