Dienstag, 15. Juni 2010


Topthema

Donnerstag, 9. Juni 2005 | Topthema

About Security #9: Sourcecode Audit — Pufferüberläufe finden

(Link zum Artikel: http://www.entwickler.de/entwicklerde/kolumnen/022161)

Wie Entwickler Pufferüberlauf-Schwachstellen in ihren Programmen finden können, erfahren Sie in dieser und der nächsten Folge von About Security.

Wo suchen?

Bei der Suche nach Pufferüberlauf-Schwachstellen bzw. allgemein nach Schwachstellen gibt es zwei Möglichkeiten: Die Suche im Sourcecode und die Analyse der Binaries. Entwickler werden meist die Suche im Sourcecode vorziehen, da ihnen dieser ja vorliegt. Unter Umständen ist für sie aber auch eine Analyse der Binaries sinnvoll, z.B. wenn Schwachstellen in eingebundenen Bibliotheken vermutet werden.

N E U ! Security aktuell
Täglich aktuelle Security-Infos!

Sourcecode Audit

Prinzipiell wird beim Sourcecode Audit der Sourcecode Zeile für Zeile nach verdächtigen Codestellen durchsucht. Wird nur nach Pufferüberlauf-Schwachstellen gesucht, werden erst alle Puffer fester Größe ermittelt und danach wird deren weitere Verarbeitung untersucht. Diese manuelle Suche ist zwar die aufwändigste Methode, aber auch die sicherste. Automatisierte Werkzeuge sind zwar beim Finden offensichtlicher Fehler sehr schnell, stoßen bei komplexen Problemen wie zusammengesetzten Schwachstellen aber an ihre Grenzen.

Für automatische Werkzeuge gibt es zwei Ansätze: Die syntaktische oder lexikalische und die semantische Analyse. Bei der syntaktischen Analyse wird nach potenziell gefährlichen Funktionen gesucht, während bei der semantischen Analyse der Datenfluss analysiert wird.

Syntaktische Analyse

Die einfachsten Hilfsmittel zur Suche nach gefährlichen Funktionsaufrufen sind die Suchfunktion des verwendeten Editors und das Tool grep. Da der Entwickler dabei nach jeder Funktion einzeln suchen muss, ist ihre Verwendung zur syntaktischen Analyse jedoch sehr mühsam. Sehr viel besser geeignet sind spezielle Programme wie z.B. flawfinder für C und C++ und RATS für C, C++, Perl, PHP und Python. Beide verwenden eine Datenbank mit potenziell gefährlichen Funktionen und geben zu den gefundenen Schwachstellen Hinweise zu möglichen Auswirkungen und Abhilfen.

Ein Beispiel

Test-Programm test.c

#include <string.h>

int main (int argc, char *argv[])
{
char puffer[8];
if (argc > 1)
strcpy (puffer, argv[1]);
return 0;
}

Ausgabe von flawfinder

ceilers flawfinder test.c 
Flawfinder version 1.26, (C) 2001-2004 David A. Wheeler.
Number of dangerous functions in C/C++ ruleset: 158
Examining test.c
test.c:6: [4] (buffer) strcpy:
Does not check for buffer overflows when copying to destination.
Consider using strncpy or strlcpy (warning, strncpy is easily misused).
test.c:4: [2] (buffer) char:
Statically-sized arrays can be overflowed. Perform bounds checking,
use functions that limit length, or ensure that the size is larger than
the maximum possible length.

Hits = 2
[...]
Not every hit is necessarily a security vulnerability.
There may be other security vulnerabilities; review your code!

Da bei der syntaktischen Analyse auch alle Funktionsaufrufe ausgegeben werden, bei denen die Parameter vorher auf die korrekte Länge geprüft werden, gibt es viele Fehlalarme.

About Security: Die komplette Serie
Semantische Analyse

Bei der semantischen Analyse wird der Datenfluss analysiert. Dies ist ein Teil des Kompiliervorgangs. Der gcc-Compiler gibt beispielsweise bei Angabe der Option -Wall auch Warnungen bei der Verwendung unsicherer Funktionen aus. Es gibt aber auch eigenständige Programme für die semantische Analyse. Ein Beispiel für ein derartiges Tool ist Splint. Splint überprüft, ob bestimmte Annahmen, die als "Annotations" im Sourcecode spezifiziert wurden, zutreffen. Die Annotations für die Funktion strcpy(s1, s2) sehen z.B. folgendermaßen aus:


void /*@alt char * @*/strcpy
(/*@unique@*/ /*@out@*/ /*@returned@*/ char *s1, char *s2)
/*@modifies *s1@*/
/*@requires maxSet(s1) >= maxRead(s2) @*/
/*@ensures maxRead(s1) == maxRead (s2) @*/;

Die requires-Klausel verlangt, dass der als s1 übergebene Puffer groß genug sein muss, um s2 aufzunehmen. Die ensure-Klausel legt fest, dass die Anzahl gelesener Zeichen von s1 und s2 nach dem Aufruf gleich groß sein muss. In Fällen, in denen die Größe von s2 unbekannt ist, sollte die Funktion strncpy(s1, s2, n) verwendet werden. Ihre Annotations sehen folgendermaßen aus:


void /*@alt char * @*/ strncpy
(/*@unique@*/ /*@out@*/ /*@returned@*/ char *s1, char *s2, size_t n)
/*@modifies *s1@*/
/*@requires maxSet(s1) >= ( n - 1 ); @*/
/*@ensures maxRead (s2) >= maxRead(s1) /\ maxRead (s1) <= n;@*/;

Die requires-Klausel verlangt, dass der als s1 übergebene Puffer groß genug sein muss, um die Anzahl zu kopierender Zeichen aufzunehmen. Die ensure-Klausel legt fest, dass nach dem Aufruf maximal n Zeichen aus s1 nach s2 kopiert wurden.

Die Methoden der Sourcecode-Analyse reichen in den meisten Fällen aus, um Schwachstellen zu finden. Liegt der Sourcecode nicht vor, hilft nur eine Analyse der Binaries. Dies ist das Thema der nächsten Folge.

Wenn Sie Fragen oder Themenvorschläge haben, können Sie diese gerne an die angegebene E-Mail-Adresse senden oder im Security-Forum einbringen!

Carsten Eilers

About Security – Übersicht zum aktuellen Thema "Eine typische Schwachstelle: Der Pufferüberlauf"

Kommentare

Folgende Links könnten Sie auch interessieren