Der Buffer Overflow ist eine Sicherheitslücke, vor der sich der C-Programmierer stets in Acht nehmen muss. Zu den jüngsten Opfern dieser Schwäche gehören zwei Linux-/Unix-Daemons.
Rsyslog
Das GPLv3-lizenzierte Rsyslog implementiert und erweitert das unter Linux und Unix verbreitete Syslog-Protokoll zum Überwachen von Systemereignissen. Die Filterfunktionen der Software helfen umfangreiche Protokolldaten effizient zu verarbeiten. Ein im September entdeckter Buffer Overflow [1] führte dazu, dass ein Angreifer das Programm zum Absturz bringen konnte.
Rsyslog erlaubt es dem Admin, jeder Nachricht ein Tag zuzuordnen, sodass sich die Logdatei später einfach mit Grep durchsuchen lässt. Für das Verarbeiten dieser Tags ist »parseLegacySyslogMsg()«
in der Datei »tools/syslogd.c«
zuständig (Listing 1). Die Funktion liest das Tag ein und legt es im Puffer »bufParseTAG«
ab, wobei sie dafür eine Maximallänge von »CONF_TAG_MAXSIZE«
Zeichen vorsieht. Der Code in Listing 1 bearbeitet alle Nachrichten, die nicht im RFC-5424-Format vorliegen, also auch solche im RFC-3164-Format.
01 [...]
02 int i; /* general index for parsing */
03 uchar bufParseTAG[CONF_TAG_MAXSIZE];
04 uchar bufParseHOSTNAME[CONF_HOSTNAME_MAXSIZE];
05 [...]
06 while(lenMsg > 0 && *p2parse != ':' && *p2parse != ' ' && i < CONF_TAG_MAXSIZE) {
07 bufParseTAG[i++] = *p2parse++;
08 --lenMsg;
09 }
10 if(lenMsg > 0 && *p2parse == ':') {
11 ++p2parse;
12 --lenMsg;
13 bufParseTAG[i++] = ':';
14 }
15 [...]
16 bufParseTAG[i] = '\0'; /* terminate string */
Der Programmierfehler tritt in der While-Schleife ab Zeile 6 auf. Die dortigen Anweisungen füllen sukzessive den Puffer »bufParseTAG«
, die Variable »i«
überwacht dabei die aktuelle Position im Puffer. Sie kommt kurz darauf ins Spiel, um an das Ende der Zeichenkette ein oder zwei zusätzliche Zeichen anzuhängen: »:\0«
oder nur »\0«
.
Zwei zu viel
Die Schwäche besteht darin, dass die While-Schleife bei langen Tag-Strings erst abbricht, wenn »i«
so groß wie »CONF_TAG_MAXSIZE«
ist (Zeile 6). Der Puffer bekommt später aber noch ein oder zwei Zeichen hinzugefügt (Zeilen 13 und 16). Einem Angreifer wäre es also möglich, mit einem Tag, das länger ist als in »CONF_TAG_MAXSIZE«
festgelegt, den Puffer um ein oder zwei Zeichen überlaufen zu lassen.
Die Auswirkungen eines Überlaufs sind plattform- und systemspezifisch: Entscheidend ist die Reihenfolge der Funktionsvariablen auf dem Stack und ob es sich um ein Little- oder Big-Endian System handelt. Beispielsweise kann es zum Überschreiben der Variablen »i«
kommen, falls sie im Speicher nach »bufParseTAG«
abgelegt ist.
Diese Sicherheitslücke betrifft die Rsyslog-Versionen 4.6.0 bis 4.6.7 sowie 5.2.0 bis 5.8.4. Auf Systemen mit einem Buffer-Overflow-Schutz wie Stack Guard kann es passieren, dass die Schutzvariable (siehe Kasten "Schutzmechanismen") direkt nach der Variablen »bufParseTAG«
im Speicher angesiedelt ist. In diesem Fall führt der Overflow zum Beenden von Rsyslog, weil Stack Guard merkt, dass diese Variable modifiziert wurde.
Es gibt Schutzmechanismen, um Overflows wie im Fall von Rsyslog zu verhindern. Dazu gehört die Software Stack Guard [2], die GCC um die entsprechende Schutzfunktion erweitert. Diese Mechanismen sortieren die Daten auf dem Stack Frame von Funktionsaufrufen um und erweitern sie um Schutzvariablen.
Bei einer Funktion wie der folgenden legt der Compiler die Variablen in einer bestimmten Reihenfolge auf dem Stack ab:
int function()
{
int a;
char b[5];
char c[4];
[...]
}
Der Stack sieht dann folgendermaßen aus:
c
b
a
[...]
Return-Adresse
Lässt ein Angreifer nun »c«
oder »b«
überlaufen, kann er »a«
verändern – unter Umständen sogar den Kontrollfluss des Programms, wenn er die Return-Adresse auf von ihm erzeugten Code lenkt.
Stack Guard und andere Buffer-Overflow-Schutzprogramme wie Pro Police oder Stack Ghost sortieren die gefährdeten Variablen um, sodass beispielsweise beim angegebenen Funktionscode der Stack mit Schutzmaßnahmen wie folgt aussähe:
a
c
b
Canary-Variable
[...]
Return-Adresse
Hier ist zum einen die Variable »a«
verschoben, sodass sie von einem möglichen Überlauf der anderen Puffer nicht betroffen ist. Zum anderen hat der Schutzmechanismus am Ende des Variablenblocks auf dem Stack eine Schutzvariable hinzugefügt, die so genannte Canary-Variable. Läuft beispielsweise die Variable »b«
über, so verändert das auch die benachbarte Canary-Variable. Sie warnt vor dem Overflow – wie der Kanarienvogel den Bergmann vor Grubengas, daher der Name.