Bufferoverloop

Een bufferoverloop (Engels: buffer overflow) is een benaming voor twee problemen die zich bij computers kunnen voordoen.

  • Gegevens stromen sneller binnen dan ze verwerkt kunnen worden.
  • Er worden data geschreven in een buffer die daarvoor te klein is. Dit veroorzaakt doorgaans onvoorspelbare problemen in de werking van het programma, tenzij de bufferoverloop nauwkeurig wordt ontworpen om een bepaald effect te bereiken.

Het eerste probleem kan zich voordoen wanneer een apparaat (bijvoorbeeld een computer) gegevens stuurt naar een ander apparaat (bijvoorbeeld een printer). De printer kan de ontvangen gegevens misschien bufferen voordat ze geprint worden, maar de grootte van de buffer is beperkt. Bij seriële communicatie is het gebruikelijk dat de ontvanger met ^Q en ^S aan de zender laat weten dat het zenden gepauzeerd moet worden.

Het tweede probleem doet zich voor als er een buffer van beperkte grootte gedeclareerd is, waarin gegevens regel voor regel gelezen worden, wanneer een regel langer blijkt te zijn dan de buffer. Een goed geschreven programma zorgt er in zo'n geval voor dat de regel bij ontvangst wordt opgesplitst.

Fouten in zulke programma's, bovendien nog meer in het geval van besturingssystemen, worden soms misbruikt door wormen, virussen en hackers om ongeoorloofde toegang te krijgen tot computersystemen.

Geschiedenis

Een van de eerste teksten over de bufferoverloop, "Smashing the stack for fun and profit", werd geschreven door Aleph One, in het E-zine Phrack. Hierin wordt uitgelegd hoe men in een slecht geschreven C-programma de buffer kan laten overlopen en de controle over het programma kan overnemen door het manipuleren van de stack.

Implicaties

Een bufferoverloop zorgt ervoor dat er gegevens worden overschreven die niet overschreven mogen worden, voornamelijk het return-adres. In het beste geval leidt dit tot een programmacrash, in het slechtste geval tot een overname door het programma door iets dat gegevens levert aan het programma. Het is daarom belangrijk programma's te maken die geen bufferoverloop veroorzaken.

Oorzaken

De oorzaak van een bufferoverloop kan het best weergegeven worden met een stukje voorbeeldcode in C:

#include <stdio.h>

void main (int argc, char *argv[])
{
    char grote_buffer [1024];               // buffer van 1024 bytes
    fgets(grote_buffer, 1024, stdin);       // verkrijg string van toetsenbord, maximaal 1024 tekens
    overloop(grote_buffer);                 // roep kwetsbare functie aan
}

void overloop(char *groot)
{
    char kwetsbaar[100];      // overlopende buffer [100 bytes]
    strcpy(kwetsbaar,groot);  // kopieer grote_buffer naar kwetsbaar
    return;                   // verlaat functie
}


Bovenstaand stuk code voert de volgende acties uit:

  1. Lees toetsenbordinvoer in grote_buffer
  2. Roep functie overloop
  3. Kopieer invoer naar kwetsbaar (100 bytes)
  4. Verlaat functie
  5. Verlaat programma

De fout ligt bij stap 3. Doordat grote_buffer groter is dan kwetsbaar, zal bij het kopiëren het stuk van grote_buffer dat niet in kwetsbaar past achter kwetsbaar geschreven worden. Dit valt alleen te merken als de toetsenbordinvoer groter dan 100 bytes is.

Omdat kwetsbaar geen globale variabele is wordt deze op de stack opgeslagen. En achter kwetsbaar staat dan het terugkeeradres van functie overloop. Dit is een getal dat aangeeft waarnaar moet worden teruggekeerd als de functie is afgelopen. Door het terugkeeradres te overschrijven kan dus het programmaverloop aangepast worden. Als men bijvoorbeeld 200 a's invoert zal geprobeerd worden terug te springen naar adres 0x61616161 (61 is de hexadecimale weergave van "a").

Misbruik

Om succesvol misbruik van een bufferoverloop te maken moet er eigen code worden uitgevoerd, soms het 'egg' (ei) genoemd. Deze code kan bijvoorbeeld in de toetsenbordinvoer zitten. Het is echter moeilijk de locatie van de invoer op de stack te bepalen. Hiervoor is echter een oplossing: na de terugkeer van de functie bevat de stack pointer (esp) het adres na waar het terugkeeradres stond. Als je dus de code na het terugkeeradres plaatst, en het terugkeeradres naar een jmp esp/call esp laat wijzen, zal de code uitgevoerd worden.

Criteria waaraan moet worden voldaan

Het terugkeeradres moet natuurlijk wel op de juiste plek staan. Het makkelijkste is om een rij opvolgende tekens te gebruiken (abcdefg...) en dan te kijken waarnaartoe wordt teruggekeerd. Ook moet de code natuurlijk wel iets zinnigs uitvoeren; het gebruikelijkste is een zogenaamde shellcode die de exploiter een commandline-interface biedt om het systeem te besturen.

Het terugkeeradres en de shellcode mogen beide niet de getallen 0x00, 0x0A, 0x0C en 0x1A bevatten, omdat het kopiëren van tekenreeksen hier stopt.

Voorkomen

Een bufferoverloop is te voorkomen door de maximumlengte van de te kopiëren tekenreeksen te controleren alvorens data te schrijven.

Zie ook