Beschäftigt man sich regelmäßig mit IT Sicherheitsthemen – etwa in dem man jede Woche den Security Now Podcast hört – gibt es immer wieder Momente in denen man völlig davon überzeugt ist, dass das Internet nur von Kaugummi und Klebeband zusammengehalten wird. Aber oft sind es auch Themen, die tief in die unheimlich komplex gewordenen Infrastukturen und Technologien führen, mit denen wir täglich hantieren. Das sind zwei Beispiele aus den letzten Wochen, die ich besonders spannend fand:
Trojan Source – UTF-8 und Tricks mit dem bidi Encoding 🔀
Aus Security Now 843: Zwei britische Forscher haben ein spannendes Paper mit dem Titel ‚Trojan Source: Invisible Vulnerabilities‚ veröffentlicht, welches im Endeffekt diese Kernaussage hat:
Bei der Prüfung eines Programmcodes kann man nicht mehr davon ausgehen, dass der Code, den man vor sich auf dem Bildschirm sieht, der gleiche Code ist, der später vom Compiler ‚gesehen‘ und in ein Programm übersetzt wird
So wie das Trojanische Pferd auch nicht das war, was es zu sein schien…Wenn man darüber einen Moment nachdenkt zieht es einem nach und nach das Fundament unter dem Füßen weg, auf dem die Überprüfung von Softwarequalität und -sicherheit von jeher stand:
Ein Mensch schaut sich in einem Editor den Quellcode, den ein anderer Mensch – oder inzwischen auch eine Maschine – geschrieben hat, versucht ihn zu verstehen und vergleicht ihn mit der Funktion, die der Code laut Pflichtenheft oder Eigenbeschreibung oder Stack Overflow Kommentar haben soll.
Im Paper werden nun unterschiedliche Verfahren beschrieben, wie man in Quellcode, der als Unicode gespeichert ist, subtile Täuschungen einschmuggeln kann. Und dabei kommt die Komplexität moderner Zeichensatzcodierungen, die den Anspruch haben letztlich jedes relevante Schriftsystem der Welt – und möglicherweise auch extraterrestrische – korrekt darstellen zu können, uns die Quere:
Neben vergleichsweise simplen Ticks wie dem Einfügen von breitenlosen und damit unsichtbaren Leerzeichen (Zero Width Space ZWSP), die Stringvergleiche scheitern lassen können, oder der Verwendung von Homoglyphen, die etwa zur Einführung alternativer Methodenaufrufe genutzt werden können, sind es die Nutzungen der Bidirectional Commands und davon gibt es einen ganzen Block:

Die lassen sich sogar verschachteln und ähnlich wie bei einem Stack in der Programmierung gibt es POP Anweisungen um Verschachtelungen wieder zu beenden und damit eine Schachtelungsebene nach oben zu gehen:

Gerade mit dem Isolates, die komplette Gruppen von Zeichen als gemeinsames Element behandeln, und Kombinationen von BiDi Anweisungen lassen sich leicht Fälle konstruieren, bei denen etwas, dass im Quellcodeeditor so aussieht:
...
/* Wenn Sicherheitsprüfung bestanden, dann return; */
if (check == true)
return;
else
throw new Exception("Keine Berechtigung!");
für den Compiler hingegen so erscheint:
...
/* Wenn Sicherheitsprüfung bestanden, dann */ return;
if (check == true)
return;
else
throw new Exception("Keine Berechtigung!");
Hier wird also aus dem scheinbaren Kommentar am Ende eine Programmanweisung, die die Logik komplett aushebelt. Das solche subtilen Änderungen manchmal weitreichende Probleme verursachen, dafür ist der goto fail;-Bug ein gutes Beispiel, den Apple vor Jahren in seinem Betriebssystem hatte. Hier sah der fehlerhafte Abschnitt so aus:
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail;
Richtig wäre aber diese Variante gewesen:
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail;
Die dritte Zeile durfte also nicht eingerückt sein. Manchmal reicht schon ein TAB um den Unterschied zu machen und in diesem Fall gab es Vermutungen, dass vielleicht jemand dafür bezahlt worden sein könnte diesen subtilen Fehler an einer entscheidenden Stelle einzubauen. Das Risiko der Entdeckung wäre gering gewesen und man hätte sich leicht damit herausreden können, dass es sich um einen Tippfehler gehandelt hat. Mit Hilfe der BiDi-Trickserei wäre dieser Fehler nicht einmal bei einem sehr sorgfältigen Code Review sichtbar gewesen.
Da die Forscher bereits reale Beispiele für die Anwendung dieser Technik gefunden haben ist das keine rein theoretische Bedrohung. Da sich solcher Code sogar per Copy-and-Paste übernehmen lässt sind beliebte Vorgehensweisen in der modernen Softwareentwicklung ebenfalls gefährdet. Also noch mehr gefährdet, als sie es immer schon waren 🙈
Was kann man tun?
Grundsätzlich wird man wohl abwarten müssen bis die Werkzeuge wie Editoren, Compilier und vielleicht auch Linter sich dieses Problems annehmen. Bis dahin gibt es vielleicht diese Faustformeln:
- Gerade in den Quellcode eingebrachte Texte wie Kommentare und Strings sind Einstiegspunkte für diese Art von Angriff
- Die Auswirkung der bidi-Tricksereien enden beim nächsten Zeilenumbruch. Eine einheitliche Quellcodeformatierung, die z. B. eingebettete Kommentare nicht in der gleichen Zeile stehen lässt wie Code, kann ein paar Varianten vermutlich aushebeln
- Bei der Nutzung von fremden Programmcodes muss man noch stärker als bisher schon auf deren Qualität achten. Was immer schon notwendig, und immer schon schwierig war
HTTP Request Smuggeling 🧱⛏️
Aus Security Now 846: Auch dieses Problem wird durch Komplexität verursacht, aber auf einer ganz anderen Ebene: Hier geht es darum, dass zwei oder mehr hintereinander geschaltete Webserver, die am Bedienen eines Webrequests beteiligt sind, das dabei verwendete HTTP Protokoll nicht einheitlich interpretieren.
Und im Endeffekt unterschiedliche Vorstellungen davon haben, welche Webrequests sie eigentlich gerade verarbeiten.
Dieses Problem ist kein ganz neues Problem, aber heute noch relevanter, da es inzwischen gängige Praxis ist, dass mehr als ein Server am Ausliefern einer Seite beteiligt ist:
- Proxy Server werden vor den eigentlichen Applikationsserver geschaltet, um die Last zu verringern
- Webserver wie Apache oder nginx liegen vor den Applikationsservern um TLS zu terminieren oder Loginfunktionen – z. B. Shibboleth – zu übernehmen
- Loadbalancer sind Netzwerkkomponenten, die die Zugriffe auf nachgelagerte Server verteilen, um Lasten gleichmäßig verarbeitbar zu machen
- Dienste wie Cloudflare bieten ihren Kunden durch vorschaltete Systeme Schutz vor Denial of Service Angriffen, die eigentlichen Server sind nicht mehr direkt erreichbar
- Und alle denkbaren Kombinationen davon….
Für die effiziente Kommunikation zwischen diesen Servern werden verschiedene Webrequests gerne hintereinander gehängt, damit sie sich schnell über die gleiche Verbindung übertragen lassen.
Aber wie schafft man es dem einen Server etwas anderes vorzugaukeln, als dem später drankommenden Server? In dem man die zwei Optionen, die der HTTP Standard als Mittel bietet um die Länge eines Request zu definieren – und damit bei hintereinander gehängten Requests zu definieren, wo der eine aufhört und der andere beginnt – so trickreich nutzt, dass die beteiligten Server zu unterschiedlichen Ansichten darüber gelangen, wo die Grenze zwischen zwei Requests liegt. Auch wenn es laut dem Standard eigentlich klar wäre, wie vorzugehen ist:
“According to the HTTP/1.1 standards, if both Content-Length and Transfer-Encoding headers are passed, Transfer-Encoding takes precedence.”
Quelle auf Medium
Auf diese Weise kann es bei anfälligen Serverkombinationen – und laut diesem Paper sind das viele – gelingen einen Zugriff auf einen Backend Server abzusetzen, der eigentlich von den davor geschalteten Servern gefiltert werden sollte. Das kann insbesondere dann relevant sein, wenn die vorgeschalteten Server das Login- und Rechtemanagement übernehmen.

Auch hier ist es nicht leicht etwas zu tun. Wie immer muss man natürlich seine Server aktuell halten, so gibt es z. B. für Tomcat inzwischen einen Patch für CVE-2021-33037, der auch mit HTTP Request Smuggeling zu tun hat. Aber auf das Verhalten vieler Komponenten, wie z. B. Loadbalancer, hat man wenig bis keinen Zugriff.
Eine Lösung ist es vielleicht die kritischen Entscheidungen einer Applikation – also die Rechte- und Zugriffskontrolle – möglichst weit nach hinten zu verlagern, bis in die Applikationsserver. Das nimmt einem Optimierungsmöglichkeiten, aber dafür ist der Server, der am Ende die Requests verarbeitet, auch derjenige, der entscheidet welche Requests zulässig sind.