Mittwoch, 8. Februar 2012 |
Integer-Schwachstellen treten häufig dann auf, wenn ein Programm Rechenoperationen mit Längenwerten durchführt, ohne dabei die Behandlung von Integer-Werten durch Compiler und Prozessor zu berücksichtigen. Besonders häufig kommt es dann zu Integerüberläufen oder Vorzeichenfehlern.
Zu einem Integerüberlauf kommt es, wenn bei einer Rechenoperation mit Integerwerten das Ergebnis größer als der maximal mögliche Wert oder kleiner als der minimal mögliche Wert wird. Dabei wird aus einer sehr großen Zahl eine sehr kleine bzw. aus einer sehr kleinen Zahl eine sehr große.
Als Beispiel kann folgender Quelltext dienen:
void test1(char* Eingabe)
{
unsigned short laenge = strlen(Eingabe) + 1;
char* puffer = (char*) malloc(laenge);
strcpy(puffer, Eingabe)
...
}
Die Anwendung berechnet die Länge des vom Benutzer eingegebenen Strings, addiert 1 zur Berücksichtigung des den String abschließenden Null-Bytes, reserviert einen Puffer der sich ergebenden Größe und kopiert die Eingabe hinein. Mit normal langen Eingaben verhält sich der Code wie vorgesehen, ein ohne Prüfung möglicher Pufferüberlauf wird verhindert.
Wird aber ein Wert eingegeben, der 65.535 Zeichen lang ist, kommt es zu
einem Integerüberlauf: Ein short-Integer ist 16 Bit lang
und kann damit Werte zwischen 0 und 65.535 aufnehmen. Wird ein String der
Länge 65.535 eingegeben, addiert das Programm zu dieser Länge 1
dazu, und der Wert wird zu 0. Dadurch wird ein Puffer der Länge 0
reserviert und die Eingabe hinein kopiert: Es kommt zu einem
Heapüberlauf. Der Code, der eigentlich einen Pufferüberlauf
verhindern sollte, hat ihn erst ermöglicht.
Vorzeichenfehler treten auf, wenn ein Programm sowohl vorzeichenlose als auch vorzeichenbehaftete Integerwerte für die Berechnung von Pufferlängen verwendet und diese Werte falsch kombiniert. Das kann entweder in Form eines direkten Vergleichs zwischen vorzeichenlosen und vorzeichenbehafteten Wert passieren, oder bei der Übergabe eines vorzeichenbehafteten Werts an eine Funktion, die einen vorzeichenlosen Wert erwartet. In beiden Fällen wird der vorzeichenbehaftete Wert als sein vorzeichenloses Äquivalent aufgefasst, wodurch ein negativer Wert zu einer großen positiven Zahl wird.
Als Beispiel kann folgender Quelltext dienen:
void test2(char* Eingabe, int laenge)
{
char puffer[32] = "";
if (laenge < 32)
strncpy(puffer, Eingabe, laenge);
...
}
Die Funktion übernimmt den vom Benutzer eingegebenen String sowie
dessen Länge als vorzeichenbehafteten Integer. Danach wird ein Puffer
mit fester Länge auf dem Stack reserviert, geprüft, ob die
Länge kleiner als die Pufferlänge ist, und nur wenn das zutrifft,
wird der Parameter in den Puffer geschrieben. Ist der Parameter
laenge eine positive Zahl, verhält sich der Code wie
erwartet und verhindert einen Pufferüberlauf.
Gelingt es einem Angreifer aber, für den Parameter laenge
einen negativen Wert zu übergeben, wird die Schutzfunktion
unterlaufen, da beide Werte vom Compiler für den Vergleich als
vorzeichenbehaftete Integer behandelt werden und ein negativer Wert immer
kleiner als 32 ist. Da strncpy einen vorzeichenlosen
Integerwert erwartet, casted der Compiler den übergebenen
vorzeichenbehafteten Wert in einen solchen: Aus dem negativen Wert wird
eine sehr große positive Zahl. Ist der vom Benutzer eingegebene Name
länger als 32 Zeichen, kommt es zum Pufferüberlauf.
Ein solcher Angriff ist nur möglich, wenn der Angreifer den Parameter
laenge manipulieren kann, z.B. weil er bereits auf dem Client
über JavaScript ermittelt und danach auf dem Server nicht mehr
überprüft wird. Ist die Größe der Integer-Variable
klein genug, z.B. short, kann auch eine Längenberechnung
auf dem Server ausgetrickst werden: Bei Eingabe eines überlangen
Namens kommt es zu einem Integerüberlauf, der den gewünschten
negativen Wert erzeugt.
Verhindert werden Integer-Schwachstellen, indem die Parameter vor ihrer Verwendung geprüft und immer auf den richtigen Type geachtet wird.
Interessant sind prinzipiell alle Parameter, in denen ein Integer-Wert an die Webanwendung übergeben wird. Dabei können die Werte auf zwei Arten übertragen werden: Zum einen als normale Zahlen in einem GET-, POST- oder Cookie-Parameter, zum anderen als Bestandteil eines größeren Objekts mit binären Daten, das z.B. von einer Clientseitigen Komponente wie einem ActiveX-Control oder einem Java-Applet übertragen wird.
Besonders interessant sind Parameter, die die Länge eines gleichzeitig übertragenen Strings enthalten. In GET-, POST- und Cookie-Parametern verrät meist der Kontext oder sogar schon der Parametername den Verwendungszweck, in binären Daten werden Längenangaben oft als Hexadezimalwert vor dem entsprechenden String gespeichert.
Für jeden in Frage kommenden Parameter müssen Werte eingegeben werden, die jeweils an den Grenzen der Wertbereiche für vorzeichenlose und vorzeichenbehaftete Integer-Werte liegen:
Werden die Daten hexadezimal übertragen, müssen sowohl Little-Endian- als auch Big-Endian-Werte eingegeben werden. Nach jeder Eingabe wird die Ausgabe der Webanwendung wie bei der Suche nach Pufferüberlauf-Schwachstellen auf ungewöhnliche Reaktionen überprüft, siehe About Security #197.
In der nächsten Folge wird die Suche nach Formatstring-Schwachstellen beschrieben.
Wenn Sie Fragen oder Themenvorschläge haben, können Sie diese gerne an die angegebene E-Mail-Adresse senden oder im Security-Forum einbringen!