Elementrica
03Fallstudien04Referenzen05Unternehmen06Aktuelles07Kontakt
PLENDE

Request Smuggling auf einem Server, den es im Netz nicht geben sollte

Penetrationstest
Request Smuggling auf einem Server, den es im Netz nicht geben sollte

Der Kunde gab uns zwei mobile Anwendungen und einen Satz Umfang: Prüfen Sie, ob sie sich knacken lassen. Die Anwendungen erwiesen sich als gut gebaut, also gingen wir einen anderen Weg.

Nicht über das Telefon, sondern über einen Entwicklungsserver, der im öffentlichen Internet stand, mit aktiviertem Debug-Modus und einer Anfälligkeit für Proxy-Desynchronisation. Darin fanden wir einen Mechanismus, der die Anfrage eines anderen Nutzers abfangen lässt.

Hier der gesamte Weg, mit einer Zuordnung zu MITRE ATT&CK und Ausschnitten zum Abhaken bei Ihnen.

Kontext

Die mobile Anwendung war der sicherste Teil des ganzen Puzzles. Das ist ein seltener Anblick. Meist ist gerade das Telefon der Ort, an dem wir liegengelassene Tokens finden, Schlüssel in Dateien, Daten in einer offenen Zwischenablage. Hier nicht. Daten lokal verschlüsselt, die gesamte Kommunikation über HTTPS, signierte Binärdateien, der Debug-Modus in der Produktionsversion abgeschaltet. Wer diese Anwendungen gebaut hat, hatte die Lektion zur mobilen Sicherheit gelernt.

Der Kunde bat uns um einen externen Test zweier nativer Anwendungen, auf iOS und Android, sowie der damit verbundenen Domains. Wir arbeiteten im Grey-Box-Modus: Wir erhielten das API-Schema und Testkonten, ohne Quellcode und ohne Konten mit erhöhten Rechten. Das Backend ist eine REST-API, dieselbe Schicht bedient beide Plattformen, in der Logik unterschieden sich iOS und Android also in nichts.

Die Herausforderung

Da die Anwendung selbst ordentlich gebaut war, mussten die interessanten Dinge irgendwo daneben sitzen. Ein mobiler Pentest, bei dem die Binärdatei und der lokale Datenspeicher sauber sind, verschiebt das Gewicht auf das, womit die Anwendung spricht: die API, den Proxy, die Serverkonfiguration.

Die mobile Schicht hielt sich an gute Praxis. Der Haken war, dass neben der Produktionsversion eine Entwicklungsversion derselben Anwendung im Internet stand. Mit einer Konfiguration, die niemand abschaltet, weil die Umgebung nicht produktiv ist. Der Debug-Modus des Django-Frameworks war aktiviert, und ein Stockwerk tiefer, in der Proxy-Schicht, eine Anfälligkeit für HTTP Request Smuggling. Die Verteidigung war nicht dumm. Sie bewachte einfach die Front, und der Eingang war eine Seitentür, die man leicht vergisst.

Was wir getan haben

Wir begannen mit der Informationsbeschaffung der mit der Anwendung verbundenen Domains und stießen schnell auf einen Entwicklungsserver. Bei jedem unbehandelten Fehler warf er eine vollständige Django-Debug-Seite aus. Eine solche Seite ist das Geständnis des Servers: Sie spuckt die Versionen von Framework und Bibliotheken aus, die Liste der installierten Komponenten und der Middleware, Pfade auf der Festplatte, interne Ports und Dienste, und an einer Stelle die Adresse und das Login eines internen Dienstes, den man aus dem Internet nicht sehen sollte. Für den Verteidiger ist das eine Liste von Dingen, die zu beheben sind. Für den Angreifer eine fertige Karte des Backends.

Der eigentliche Eingang lag jedoch in der Proxy-Schicht. Am Anmelde-Endpunkt des Administrationspanels war der Server anfällig für Anfrage-Desynchronisation in der Variante CL.TE. Der Reverse Proxy liest die Länge der Anfrage aus dem Header Content-Length, das Backend aus Transfer-Encoding: chunked. Wenn beide Header etwas anderes sagen, wird unterwegs aus einer Anfrage zwei, und die zweite behandelt das Backend, als käme sie direkt vom Proxy, ohne erneute Prüfung.

POST /admin/login/?next=/admin/ HTTP/1.1
Host: dev.klient.example
Content-Type: application/x-www-form-urlencoded
Content-Length: 6
Transfer-Encoding: chunked

0

X

Der Proxy sieht sechs Byte Inhalt und lässt das Ganze durch. Das Backend beendet die Anfrage bei "0" und lässt "X" als Anfang der nächsten Anfrage in der Warteschlange stehen. Dieses "X" hängt sich vorn an die Anfrage der nächsten Person, die auf diesen Server trifft.

Zuerst bestätigten wir, dass sich der Server zwingen lässt, unsere Anfrage in seiner eigenen Antwort zurückzuschicken. Bei der Panel-Anmeldung kamen der Wert des E-Mail-Feldes und der Weiterleitungsparameter im Antwortkörper zurück. Das bedeutete, dass sich der eingeschmuggelte Ausschnitt später auslesen ließ. Dann griffen wir zu dem Endpunkt, der die Profildaten eines Nutzers speichert, fingen die Anfrage eines anderen Testkontos ab und schrieben ihre ersten 150 Byte in eines der Felder. Klar gesagt: Eine fremde Anfrage fiel uns in ein Feld, das wir anschließend selbst auslesen konnten.

Das Konto haben wir nicht übernommen. Der Umfang endete genau dort, wo es am interessantesten wird, und 150 Byte einer fremden Anfrage sind noch kein fremdes Konto. Aber den Rest des Weges beschrieben wir Schritt für Schritt. In der API gab es einen Endpunkt, der ein Textfeld mit einer festgelegten unteren Längengrenze und ohne obere annahm. Ein solches fehlendes Limit macht aus dem Feld einen Puffer: Man kann darin einen deutlich längeren eingeschmuggelten Ausschnitt parken und ihn später auslesen, samt einem Autorisierungstoken, das einem anderen Nutzer gehört. Von dort ist es bis zur Kontoübernahme nicht mehr weit.

Die gesamte Kette in der Sprache von MITRE ATT&CK:

Technik (MITRE ATT&CK)Einsatz im Test
T1592.002 Gather Victim Host Information: SoftwareAuslesen der Versionen von Framework, Bibliotheken und Integrationen von der Debug-Seite
T1595.002 Active Scanning: Vulnerability ScanningErkennen der CL.TE-Desynchronisation und von Fehlkonfigurationen auf dem Dev-Host
T1190 Exploit Public-Facing ApplicationAusnutzen der Desynchronisation auf dem öffentlich exponierten Entwicklungsserver
T1557 Adversary-in-the-MiddleAbfangen der Anfrage eines anderen Nutzers durch Einreihen einer eingeschmuggelten Anfrage
T1589.002 Gather Victim Identity Information: Email AddressesEnumeration vorhandener E-Mail-Adressen über unterschiedliche Registrierungsantworten
T1539 Steal Web Session CookieEin Weg zur Übernahme des Tokens eines anderen Nutzers, verlängert durch das fehlende Sitzungs-Timeout

Die Registrierung verriet nebenbei, welche E-Mail-Adressen bereits im System sind. Eine neue Adresse gab 201 zurück und legte ein Konto an, eine vorhandene Adresse 400 mit dem Hinweis, dass dieser Nutzer bereits existiert. Zwei verschiedene Antworten erlauben es, Konten direkt zu enumerieren: Man kann in Ruhe eine Liste echter Konten aufbauen und erst darauf weitere Angriffe richten, etwa Password Spraying.

POST /rejestracja HTTP/2
Host: dev.klient.example
Content-Type: application/json

{"email":"nieznany@klient.example"}   ->  201 Created      (konto założone)
{"email":"istnieje@klient.example"}   ->  400 Bad Request  (adres już w systemie)

Dazu kam noch eine Sache aus der Sitzungsschicht: Das Authentifizierungstoken ließ sich nach einer langen Zeit der Untätigkeit erneut verwenden. Für sich genommen eine Kleinigkeit, aber im Verbund mit dem Abfangen fremder Anfragen verlängert es das Fenster, in dem ein übernommenes Token noch funktioniert.

Das Ergebnis

Aus dem gesamten Test gingen ein hohes Risiko und sechs mittlere hervor, dazu ein längerer Rattenschwanz an Kleinigkeiten. Das hohe Risiko ist die beschriebene Kette: ein öffentlicher Entwicklungsserver, der Debug-Modus und die Proxy-Desynchronisation, die zusammen jemandem von außen Zugriff auf fremde Anfragen geben. Die mobile Schicht, die den Kunden am meisten beschäftigte, bestand den Test sauber.

Die Empfehlungen ließen wir als Liste von Tätigkeiten zum Abhaken stehen, mit einer konkreten Maßnahme bei jedem Punkt:

  • Erzwingen Sie HTTP/2 auf dem gesamten Kommunikationsweg, und wo Sie auf HTTP/1.1 heruntergehen müssen, validieren Sie die umgeschriebene Anfrage gegen die Spezifikation und schließen Sie die TCP-Verbindung bei jeder Mehrdeutigkeit.
  • Nehmen Sie den Entwicklungsserver aus dem öffentlichen Internet oder verstecken Sie ihn hinter einer IP-Positivliste oder einem VPN.
  • Schalten Sie den Debug-Modus auf allem ab, was von außerhalb des internen Netzes erreichbar ist.
  • Setzen Sie serverseitiges Ungültigmachen der Sitzung nach Untätigkeitszeit und nach absoluter Zeit, sodass ein altes Token einfach aufhört zu funktionieren.
  • Vereinheitlichen Sie die Meldungen von Registrierung und Anmeldung, damit die Antwort für eine vorhandene und eine nicht vorhandene Adresse identisch aussieht.
  • Setzen Sie eine obere Längengrenze auf die von der API angenommenen Textfelder, damit keines von ihnen als Puffer dienen kann.

Dieser Test zeigt eine bestimmte Falle gut. Das Team steckte viel Arbeit in die Sicherheit der mobilen Anwendung, und das zu Recht. Die Sache ist die: Ein Angreifer geht nicht dorthin, wo Sie die meiste Arbeit hineingesteckt haben. Er geht dorthin, wo gerade ein vergessener Server mit der Konfiguration vom letzten Quartal steht. Wie viele Ihrer Entwicklungsumgebungen sind in diesem Moment aus dem öffentlichen Internet erreichbar? Wenn Sie diese Zahl nicht auswendig kennen, ermitteln wir sie gemeinsam.

Die Anwendung war bis aufs letzte Detail dicht, und der Eingang lag trotzdem seitlich. Kennen Sie all Ihre Türen?

Vereinbaren Sie ein Gespräch mit einem Berater. Gemeinsam legen wir den Umfang eines Tests Ihrer mobilen Anwendungen und ihres Backends (API, Proxys, Dev-Server) fest und wie eine solche Kette bei Ihnen aussehen könnte.

Vorheriger
Ein Betriebskonto, vollständige Übernahme der Plattform
Nächster
Fünf High-Schwachstellen, kein Weg hinein

Alle Fallstudien sind nach Sektor anonymisiert, ohne Namen, Daten oder Angaben, die den Kunden identifizieren könnten, gemäß der Vertraulichkeit. Wir veröffentlichen niemals echte Schwachstellen oder technische Kundendaten.