Moderne Browser setzen zunehmend auf Cookie-Präfixe wie __Host und __Secure, um Sitzungen besser zu schützen und potenziellen Angreifern das Einschleusen manipulierter Cookies zu erschweren. Doch die Umsetzung ist nicht immer so lückenlos, wie es scheint: Unterschiede in der Logik von Browsern und Servern können dazu führen, dass diese Schutzmechanismen umgangen werden.
Cookies mit Unicode-Leerzeichen überschreiben
Zakhar Fedotkin von PortSwigger beschreibt, wie sich durch den Einsatz von Unicode-Leerzeichen Cookie-Präfixe umgehen lassen.
Die Präfixe wurden mit RFC 6265bis eingeführt, um die Sicherheit zu erhöhen:
-
Ein Cookie mit dem Präfix
__Host-darf ausschließlich für den Host selbst gesetzt werden und nicht über Subdomains hinweg. -
Ein Cookie mit dem Präfix
__Secure-muss von einer sicheren Quelle stammen.
Browser erzwingen diese Vorgaben, um Angriffe wie Cookie-Tossing oder Session-Fixation abzuwehren. Doch Unterschiede in der Art, wie Browser und Server Cookies kodieren und interpretieren, können zu gefährlichen Schwachstellen führen.
Das ursprüngliche RFC 6265 definiert den Cookie-Header nicht als Zeichen, sondern als Abfolge von Oktetten. Der Browser sendet rohe Bytes, die der Server in eine Zeichenfolge übersetzt. Wenn beide Seiten diese Bytes unterschiedlich deuten, entstehen Parsing-Diskrepanzen.
Über die UTF-8-Kodierung lässt sich ein eingeschränktes Cookie – etwa mit dem Präfix __Host- – so tarnen, dass der Browser es nicht als geschützt behandelt, während der Server es nach der Dekodierung dennoch als solches interpretiert.
Hier ist ein minimaler Proof of Concept, der dieses Verhalten demonstriert:
Dieses Cookie mit einem vorangestellten Leerzeichen wird vom Browser als Wert ohne Präfix und ohne Einschränkung interpretiert und daher an alle Subdomains innerhalb des Bereichs der Zieldomain gesendet.
Während des Testens wurde festgestellt, dass bestimmte serverseitige Frameworks wie Django und ASP.NET vor der Verarbeitung eine Normalisierung und Kürzung der Cookie-Namen vornehmen. Insbesondere wenn der Server U+2000 als Leerzeichen interpretiert, entfernt er dieses, wodurch ein Cookie-Name entsteht, der __Host-name entspricht.
Django verwendet die in Python integrierte Methode .strip(), um Cookie-Schlüssel und -Werte zu verarbeiten. Diese Methode entfernt eine Vielzahl von Unicode-Leerzeichen, darunter [133, 160, 5760, 8192–8202, 8232, 8233, 8239, 8287, 12288], und behandelt sie effektiv als Leerzeichen.
Interessanterweise behandelt Safari diesen Fall anders. Es unterstützt keine Multibyte-Unicode-Leerzeichen in Cookie-Namen, wodurch Werte wie U+2000 nicht verwendet werden können. Einzelbyte-Zeichen wie U+0085 (NEL) und U+00A0 (nicht trenngendes Leerzeichen) sind jedoch weiterhin zulässig.
Überschreiben von Cookies mit Legacy-Parsing
Zusätzlich zu Unicode-Tricks kann auch das Legacy-Cookie-Parsing-Verhalten missbraucht werden, um Präfix-Schutzmaßnahmen zu umgehen. Wie im vorherigen Blogbeitrag gezeigt, wechseln einige Java-basierte Webserver wie Apache Tomcat und Jetty zu einem Legacy-Parsing, wenn ein Cookie-Header mit $Version=1 beginnt. In diesem Modus kann eine einzelne Cookie-Zeichenfolge als mehrere separate Cookies interpretiert werden. Das folgende JavaScript setzt beispielsweise ein Cookie, das ein gefälschtes __Host- Paar enthält:
Dadurch kann der Angreifer die Präfixprüfungen des Browsers umgehen und Cookies mit hohen Berechtigungen aus einer Subdomain oder über eine unsichere Quelle einschleusen.
Vollständiges Angriffsszenario
Angenommen, Sie entdecken eine XSS-Sicherheitslücke, bei der ein Cookie-Wert ohne ordnungsgemäße Escape-Zeichen in eine Webseite übernommen wird. Die Anwendung verwendet ein Cookie mit dem Präfix „__Host-“, das normalerweise aufgrund von browser-seitigen Einschränkungen das Überschreiben durch nicht vertrauenswürdige Subdomains verhindert. Mit einer der zuvor beschriebenen Techniken injizieren Sie jedoch mithilfe von JavaScript ein gefälschtes „__Host-name“-Cookie:
Der Browser, der nicht weiß, dass dieses Cookie dem geschützten Cookie entspricht, akzeptiert es und fügt sowohl das ursprüngliche als auch das vom Angreifer kontrollierte Cookie in die Anfrage ein. Über das Netzwerk sendet der Browser den folgenden Header:
Wenn diese Anfrage das Backend erreicht, analysiert der Server den Cookie-Header. Wenn mehrere Cookies mit demselben Namen vorhanden sind, lösen viele Frameworks, darunter auch Django, den Konflikt, indem sie nur einen Wert akzeptieren, in der Regel den zuletzt auftretenden. In diesem Fall hat der vom Angreifer kontrollierte Wert Vorrang.
Wenn die Anwendung diesen Cookie-Wert ohne ordnungsgemäße Kodierung in die Antwort zurückgibt, entsteht eine Cross-Site-Scripting-Schwachstelle. Wenn dasselbe Cookie für den CSRF-Schutz oder die Sitzungsidentifizierung verwendet wird, kann dieses Verhalten auch zu einer Sitzungsfixierung oder anderen Pfaden zur Privilegienerweiterung führen.
Django hat auf Schwachstellenbericht geantwortet:
Die offizielle Django-Dokumentation enthält eine Warnung davor, Cookies von nicht vertrauenswürdigen Subdomains zuzulassen, da dies anfällig für Angriffe ist: https://docs.djangoproject.com/en/5.0/topics/http/sessions/#topics-session-security. Da dieser Angriff darauf basiert, wird dies nicht als Sicherheitslücke behandelt.
Zusammenfassung
Ein und dasselbe Cookie kann vom Browser und vom Backend unterschiedlich interpretiert werden. Diese Diskrepanz kann die Vertraulichkeit und Integrität von Cookies unbemerkt beeinträchtigen, selbst wenn auf der Browserseite die stärksten Schutzmaßnahmen vorhanden sind.
Weitere Tipps & Themen
Bild/Quelle: Pixabay
