Einführung
Am 22. Juli 2021 fusionierte der beliebte Apache HTTPD-Webserver in einer übergeben. die die Funktion ap_getparents()
mit einer neuen Funktion namens ap_normalize_path()
. Diese neue Funktion wurde als effizientere und standardisierte Methode für die Normalisierung von Rohpfaden im URI einer HTTP-Anfrage angepriesen. Am 15. September wurden diese Änderungen in den Stamm verschmolzen und mit der Version 2.4.49 gekennzeichnet.
Was ist das Problem?
Am 29. September, vierzehn Tage nach der Veröffentlichung von Version 2.4.49, meldete ein cPanel-Ingenieur namens Ash Daulton, dass ein Angreifer diese neue Logik nutzen könnte, um einen Path-Traversal-Exploit auszuführen, der bei Erfüllung bestimmter Konfigurationsbedingungen zur Remotecodeausführung (RCE) führen könnte.
Censys fand 75.085 eindeutige Hosts mit 136.469 Diensten, die sich mit dieser einfachen Suchabfrage als Apache HTTPD Version 2.4.49 identifizierten. Und obwohl die meisten Installationen dieses Dienstes nicht von Haus aus anfällig sind, hat Censys einige Anbieter identifiziert, die eine laxe Konfiguration ausliefern, die den Fehler ermöglicht. Einer dieser Anbieter, Control Webpanel, mit über 22.000 Hosts unter CentOS, scheint aufgrund seiner Konfiguration ein wahrscheinliches Ziel für diesen Angriff zu sein.
Einige Forscher haben festgestellt, dass nach dieser Sicherheitslücke aktiv gescannt wird, obwohl keine endgültige Aussage darüber gemacht wurde, ob die Scans zu einer aktiven Ausnutzung führen.
Standardmäßig wird die folgende Zugangskontrollkonfiguration eingerichtet:
DocumentRoot "/usr/local/apache2/htdocs"
<Directory />
AllowOverride none
Require all denied
</Directory>
<Directory "/usr/local/apache2/htdocs">
AllowOverride None
Require all granted
</Directory>
Wenn ein Nutzer Folgendes anfordert "/index.html"
würde der Server das DocumentRoot (/usr/local/apache2/htdocs
) und anhängen "/index.html"
zu, was zu der Datei "/usr/local/apache2/htdocs/index.html"
. Da eine Regel den Zugriff auf alle Dateien innerhalb des "/usr/local/apache2/htdocs"
würde der Antrag genehmigt werden.
Würde hingegen ein Nutzer eine Anfrage stellen "/../../../../foobar"
würde der Server die "/../../../.."
über ap_normalize_path()
und fügen das Ergebnis an die DocumentRoot
, was zu "/usr/local/apache2/htdocs/foobar"
; eine Datei, die im Dateisystem nicht existieren sollte.
Da aber ap_normalize_path()
prüft nicht korrekt auf Doppelpunkte, wenn es als ascii-hex kodiert ist, z.B., "%2e%2e"
wird diese Normalisierungsfunktion die Punkte darstellen und den Pfad nicht entfernen. Aber auch nach dem Rendern der Doppelpunkte sollten die Standard-Zugriffskontrollen die Ausführung der meisten Pfadübergänge verhindern. Nur wenn ein Administrator explizit die Konfiguration "Require all granted"
auf einem geschützten Verzeichnis, dass die Dinge aus dem Ruder laufen können; d.h.,
<Directory />
AllowOverride none
Require all granted
</Directory>
Diese fehlerhafte Konfiguration, kombiniert mit Apache's mod_cgi
Modul, hat das Potenzial, diese einfache Pfadumgehungsschwachstelle in einen vollwertigen RCE-Angriff (Remote Command Execution) zu verwandeln.
Ein tieferer Blick
Während diese neue ap_normalize_path()
Funktion hier eine Rolle spielt, ist sie nicht gerade die verursachen der Schwachstelle. Es ist eher so, dass diese Funktion dazu beigetragen hat, ein bereits bestehendes Problem ans Licht zu bringen. Ich meine, der Beweis liegt im Namen: Es heißt "normalize", nicht "sanitize". Aber diese Änderung hatte unbeabsichtigte Nebeneffekte für andere Funktionen, die sich auf diese frühere Iteration, die die Daten bereinigte, verlassen haben.
Das eigentliche Problem ist in einer abstrahierten Funktion des Betriebssystems versteckt, die apr_filepath_merge()
und genauer gesagt, wie diese Funktion aufgerufen wird. Doch bevor wir darüber sprechen wie diese Funktion ist genannt.müssen wir verstehen was diese Funktion tut. In der Dokumentation heißt es, dass diese Funktion funktioniert:
"Zusätzlichen Dateipfad in den zuvor bearbeiteten Wurzelpfad einfügen"
Das Hauptziel dieser Funktion ist es, einen Basispfad (wie eine apache DocumentRoot
) und einen Pfad, der in der URI einer Anfrage übergeben wird, und fügen sie zusammen, um die Eingabe-URI auf einen voll qualifizierten Pfad auf dem Server abzubilden.
Hier ist eine sehr einfache Übersetzung dessen, was diese Funktion tut:
final_path = split("/usr/local/apache2/htdocs", "/") // result: ["usr", "local", "apache2", "htdocs"]
tokens = split("usr/bin/..", "/") // result: ["usr", "bin", ".."]
for each path_part in tokens:
if path_part == "..":
// remove the last segment in the final_path array
final_path[length(final_path)] = null
else:
final_path.append(path_part)
return final_path.Join("/") // returns /usr/local/apache2/htdocs/usr
Wenn diese Funktion vom Standard-Apache-Handler aufgerufen wird (ap_core_translate()
), wird zuerst der Pfad der Eingabeanforderung genommen und der erste Schrägstrich übersprungen, um zu zeigen, dass es sich um einen relativen Pfad von der DocumentRoot
. Wenn der Server zum Beispiel mit DocumentRoot "/usr/local/apache2/htdocs"
und die eingehende Anfrage lautet:
GET /foo/bar HTTP/1.0
Die apr_filename_merge()
wird folgendermaßen aufgerufen:
char * path = "/foo/bar";
char * root = "/usr/local/apache2/htdocs";
char * output;
while (*path == '/') {
++path;
}
int rv = apr_filepath_merge(&output, root, path, APR_FILEPATH_SECUREROOT, r->pool));
Dies würde dazu führen, dass die output
Puffer enthalten: /usr/local/apache2/htdocs/foo/bar
. Der Grund dafür, dass diese Aufforderung an apr_filepath_merge
ist nicht anfällig für den Path-Traversal-Angriff ist hauptsächlich auf die Flagge APR_FILEPATH_SECUREROOT
gesetzt wird. Dies führt dazu, dass die Funktion fehlschlägt, wenn der Pfad, den wir versuchen hinzuzufügen, außerhalb des Verzeichnisses "/usr/local/apache2/htdocs" liegt.
Aber nicht jede Anfrage wird vom Standard-Handler bearbeitet, einige werden von Modulen bearbeitet. Zum Beispiel kann der mod_alias
Modul muss zunächst den Eingabepfad mit einem Basispfad abgleichen, und dieser Basispfad muss zu einem echten Pfad erweitert werden. Nehmen Sie die folgende Konfiguration:
<IfModule alias_module>
ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
</IfModule>
Dies besagt, dass jeder eingehende URI-Pfad, der mit "/cgi-bin/" beginnt, eigentlich in das Verzeichnis "/usr/local/apache2/cgi-bin/" gehen sollte. Die Art und Weise, wie der URI in eine Datei übersetzt wird, unterscheidet sich also von dem, was in ap_core_translate()
. Erstens, mod_alias
iteriert über eine Liste von alias_entry
mit dem folgenden Format:
alias_entry entry = {
.real = "/usr/local/apache2/cgi-bin/",
.fake = "/cgi-bin/",
};
Er schaut sich das Präfix des eingehenden Anforderungspfads an, und wenn es mit dem fake
Element, d. h. "/cgi-bin/", wird der Inhalt des eingehenden URI an das real
Element, das seinerseits zum Aufruf von apr_filepath_merge
:
char * output_path = NULL;
prefix_len = alias_matches(request->uri, alias->fake);
if (prefix_len > 0) {
// Append the contents of the request URI after the prefix to the end of the "real" element
output_path = apr_pstrcat(request->pool, alias->real, request->uri + prefix_len, NULL);
// if alias->real IS "/usr/local/apache2/cgi-bin/"
// AND
// if request->uri IS "/cgi-bin/../../../etc"
// THEN
// output_path IS "/usr/local/apache2/cgi-bin/../../../etc"
}
if (output_path != NULL) {
char * newpath = NULL;
int ret = apr_filepath_merge(&newpath, "/usr/local/apache2", output_path, 0, p);
if (newpath != NULL && ret == APR_SUCCESS) {
return newpath; // newpath IS /usr/etc
}
}
Wie Sie vielleicht schon bemerkt haben, ist die APR_FILEPATH_SECUREROOT
Flagge wurde nicht an apr_filepath_merge
wie es in ap_core_translate()
, was bedeutet, dass der Aufruf nicht fehlschlägt, selbst wenn der URI Doppelpunkte enthält - also mod_alias
ist anfällig für den Path-Traversal-Angriff.
Aber um es ganz klar zu sagen: Nur weil diese Funktion das Traversal ermöglicht, muss der Server so konfiguriert sein, dass er den Zugriff auf das durchsuchte Verzeichnis erlaubt. Mit dem obigen Beispiel von "/usr/etc
"muss ein Administrator dem Server ausdrücklich den Zugriff auf das Verzeichnis "/usr/etc" mit der Einstellung "Require all granted" erlauben. Andernfalls scheitert die Anfrage an einer zweiten Prüfung in der Funktion ap_run_access_checker_ex()
.
Warum ist das wichtig?
Wenn die Bedingungen stimmen, kann ein Angreifer diese Pfadumgehungsschwachstelle ausnutzen, um einen beliebigen Befehl mit denselben Berechtigungen wie der Benutzer, der den Dienst ausführt, auszuführen. Ein Angreifer kann dann diesen Zugriff nutzen, um erweiterte administrative Rechte zu erlangen, wenn auf dem System nicht gepatchte lokale Sicherheitslücken vorhanden sind.
Da diese Sicherheitslücke leicht auszuführen ist und Proof-of-Concept-Exploits in den sozialen Medien und auf anderen Plattformen leicht zu finden sind, müssen wir davon ausgehen, dass böswillige Akteure bereits damit begonnen haben, anfällige Server in freier Wildbahn auszunutzen.
Was kann ich dagegen tun?
- Administratoren sollten sofort auf HTTPD Version 2.4.51 aktualisieren.
- Überprüfen Sie, ob Ihr Host in der Liste der potenziell gefährdeten Server aufgeführt ist.
- Censys ASM-Kunden wurden per E-Mail über alle Hosts benachrichtigt, die als anfällig identifiziert wurden. Darüber hinaus können ASM-Kunden verwundbare Anlagen über den folgenden Link finden.
Aktualisierung 10-07-2021 (7:30 PM EST): Die Entwickler des CentOS Webpanel haben eine Aktualisierung vorgenommen. Manuelles Ausführen /scripts/update_cwp
wird der Apache-HTTPD-Server auf die Version 2.4.48 zurückgesetzt.