Aus Linux-Magazin 12/2013

Sicherheitsrisiken in HTML-5-Websockets

© tlorna, 123RF.com

Ein Artikel im Linux-Magazin 08/13 brachte einen Magazin-Autor auf die Idee, Web RTC genauer unter die Lupe zu nehmen. Heraus kamen gravierende Sicherheitslücken, die offensichtlich so gewollt sind.

Am Anfang sollte es nur ein kleines Experiment sein, um der Web-RTC-Technologie, die der Linux-Magazin-Artikel [1] vorstellte, mit einem System aus Tomcat 7, Javascript und Apache mal aus Sicherheitsaspekten auf den Zahn zu fühlen. Schnell zeigte sich Überraschendes: Auch wenn die Javascript-Dateien auf einem separaten Apache-Server lagen, also nicht auf dem Tomcat Application Server, kam die Websocket-Verbindung zustande – ein klarer Verstoß gegen die Same Origin Policy. Die besagt nämlich, dass Javascript nur Verbindungen zu jenem Server erlaubt, von dem das Skript stammt. Doch die meisten gängigen Browser ignorieren diese Vorgabe – absichtlich und mit dem Segen der Hersteller.

Ein simpler Host- und Portscanner

Das jedoch lässt sich sehr leicht von böswilligen Angreifern ausnutzen: Fürs erste Beispiel dient die Idee, einen Host- und Portscanner zu implementieren. Versucht der Browser mit der Websocket-Connect-Methode eine Verbindung aufzubauen, sind folgende Fehlerfälle möglich:

  • Der Host ist nicht erreichbar.
  • Der Host ist zwar erreichbar, aber auf dem angesprochenen Port lauscht kein Dienst.
  • Auf dem angesprochenen Host und Port lauscht zwar ein Dienst, aber kein Webserver.
  • Es lauscht zwar ein Webserver, aber dieser versteht das Websocket-Protokoll nicht.
  • Der Zugriff wird durch den Webserver oder den Webproxy verboten.

Manchmal lässt die Fehlermeldung, die der Aufruf zurückgibt, Rückschlüsse darauf zu, was am anderen Ende der Verbindung geschieht. Mit einer geeigneten Anzahl von Tests könnte ein Angreifer herausfinden, welche Ports und Hosts auf dem Zielsystem ansprechbar sind. Dazu bringt er sein Opfer dazu, den Code in den Browser zu laden. Dieser scannt dann das lokale Netz und lädt die Ergebnisse zum Angreifer hoch.

Leider unterschieden sich im Test die Fehlercodes, die der Client in den fünf Fällen ausgab, nicht immer: Nur wenn der Verbindungsaufbau in einen Timeout lief, ließ sich das anhand der entstehenden Verzögerung erkennen.

Misst das Programm die Zeit zwischen dem Auslösen der Anfrage und dem Eintreffen der Antwort oder des Fehlers, kann der Angreifer zwischen erreichbaren und nicht erreichbaren Hosts unterscheiden. Allerdings darf er diese Technik nicht zu schnell hintereinander für denselben Host anwenden, da sonst die IP-Adresse in der lokalen ARP-Tabelle als nicht erreichbar markiert ist und der Fehlerfall deshalb sofort eintritt.

Ebenfalls Vorsicht ist geboten, wenn die Verbindungen über Proxys laufen – dann gelten die gemessenen Zeiten relativ zum Proxy, nicht zum Client. In allen anderen Fällen wird der Versuch schlicht erfolgreich oder nicht erfolgreich beendet, mehr oder weniger unmittelbar und ohne Anzeichen einer Ursache.

Ein schneller Test zeigt den Fehler

Am einfachsten lässt sich die Fehlerart über einen Event Handler erkennen. Listing 1 zeigt ein einfaches Beispiel, in dem der Code sequenziell scannt. Das Codefragment holt sich über die Methode »getNextIP()« die nächste zu scannende Adresse. Dann speichert es die aktuelle Zeit und versucht eine Websocket-Verbindung zur Adresse aufzubauen. Der darunter definierte Error Handler vergleicht die gespeicherte Zeit mit der aktuellen und speichert das Ergebnis in einem Hash mit dem Schlüssel der IP-Adresse. Schließlich ruft sich die Routine selbst auf, um den Vorgang für die nächste Adresse zu wiederholen.

Listing 1

Der Event Handler

01 function getNextIP()
02 {
03   var network = networklist[netindex];
04
05   if(network.length == ipindex)
06   {
07     network = networklist[++netindex];
08     if(network == null)
09     {
10       dumpResults();
11       return null;
12     }
13     else
14       ipindex = 0;
15   }
16   return(network[ipindex++]);
17 }
18
19
20 function scan
21 {
22   var currentip=getNextIP();
23   if(currentip != null)
24   {
25   var now=new Date();
26   var channel = new WebSocket("ws://"+currentip+":8000", 'wstest');
27   channel.onerror = function(evt)
28   {
29       var errTimestamp = evt.timeStamp;
30     if(navigator.userAgent.search("Firefox")>0)
31     errTimestamp = Math.floor(errTimestamp/1000); // Firefox macht das in Nanosekunden
32       var timediff = errTimestamp - now;
33       results[currentip] = timediff;
34       scan();
35   };
36
37   channel.onopen = function()
38   {
39       results[currentip] = "up+websocket";
40       scan();
41   };
42   }
43 }

Zielerfassung mit Web RTC

Da der Angreifer bei dieser Methode relativ blind um sich schießt, erscheint es hilfreich, wenn er den Adressenbereich, der gescannt werden soll, einschränken kann. Erst hier kommt Web RTC ins Spiel. Bei der Initialisierung sammelt der Client in Firefox und Chrome die lokalen und die externen IP-Adressen und ermöglicht es, sie per Javascript abzurufen (Listing 2). Das Skript muss jetzt nur noch den String in der Variablen »Description« nach RFC-1918-Netzen durchsuchen, die üblicherweise die LAN-Adressen eines Clients anzeigen und als Basis für den Scan dienen können.

Listing 2

Lokale IP-Adressen in Firefox finden

01 var serv=null;
02 pc = new mozRTCPeerConnection(serv);
03 pc.createOffer(function(sd)
04     {
05      pc.setLocalDescription(sd);
06      description = sd.sdp;
07     }, null, {'mandatory': {'OfferToReceiveAudio': true,
08   'OfferToReceiveVideo': true} }
09 );

XMLHTTPRequest

Neben Websockets als Kommunikationsmethode, die voraussetzen, dass der Server am anderen Ende der Verbindung das Websocketprotokoll auch versteht, gibt es noch eine weitere Möglichkeit, Verbindungen aus Javascript heraus aufzubauen: die Klasse »XMLHTTPRequest« . Sie ist dazu gedacht, dass beispielsweise Json-basierte Applikationen kompliziertere Requests (also auch »POST« ) absetzen können, um so von ihrem zugehörigen Applikationsserver Daten zu bekommen, die sich dann in Javascript auswerten lassen.

Auch »XMLHTTPRequest« sollte sich aus Sicht der Sicherheit an die Same-Origin-Regel halten – tut es aber nicht. Führt ein Skript »XMLHTTPRequest« zu einer anderen Site aus, löst das zwar auch einen Fehler aus, bei dem je nach Browser sogar ersichtlich ist, dass der Grund des Fehlers der Verstoß gegen eben diese Regel ist. Beobachtet man aber die Logdatei des Webservers, der ja vermeintlich nicht angesprochen wurde, oder schaut der Admin mit Wireshark zu, erlebt er eine Überraschung: Der Request findet trotzdem statt.

Die Scanroutine aus Listing 1 lässt sich durch einen Scan mit »XMLHTTPRequest« nachbilden, da auch hier ein Error Handler (hier heißt er »onreadystatechange« ) ausgelöst wird. Und dann sind im Gegensatz zu den Websockets mit dieser Routine schwere Schäden möglich, da sich komplexe Requests auslösen lassen, in denen Angreifer Angriffsvektoren aus den OWASP Top Ten [3] integrieren.

Das Skript des Angreifers sieht zwar nicht selbst die Ergebnisse seiner Versuche, aber neben den bereits beschriebenen Möglichkeiten, die Adressbereiche einzuschränken, sind Standard-Hostnamen wie »wiki« oder »intranet« gute Kandidaten, um einen Treffer zu landen.

Zur Rede gestellt

Wer nun meint, das Verhalten sei ein Bug, der sieht sich getäuscht: Als sich der Autor dieses Artikels daran machte, bei Google, Microsoft und Mozilla einen Bugreport zu eröffnen, lautete die Antwort schlicht, dieses Verhalten sei gewollt und von der Cross Origin Ressource Sharing Policy [4] des W3C abgedeckt. Diese Policy gestattet es, dass ein von »http://example.org« heruntergeladenes Skript Zugriff auf »http://beispiel.org/serverscript« über »XMLHTTPRequest« oder andere Calls erhält.

Und damit der Browser weiß, dass er das darf, landet gar ein neuer Header im HTTP-Protokoll, der »Access-Control-Allow-Origin:« heißt, gefolgt von der URL, von der aus der Zugriff erfolgen kann. Es klingt absurd: Da der Browser erst durch den Server mitgeteilt bekommt, ob er überhaupt Requests ausführen darf, ergibt sich ein Henne-Ei-Problem.

Henne und Ei

Der anfragende Browser erfährt erst nach der Antwort, ob er die Frage überhaupt stellen darf. Dem versucht die Spezifikation damit zu begegnen, dass der Client eine einfache Preflight-Anfrage erstellen darf, um in Erfahrung zu bringen, ob der folgende komplexere Zugriff überhaupt erlaubt ist.

Dabei gibt es wohl noch einige Unklarheiten: Laut Aussage des Mozilla-Teams zählen »POST« -Requests nicht dazu, laut Dokument schon. Der Bearbeiter des Bugs aus dem Mozilla-Team bemerkte auch, dass Firefox bis Version 24 erst einen Options-Request ausführt. Der neue Browser dagegen sendet den Origin-Header mit, sodass sich eine Applikation auf dem Server gegen Requests fremden Ursprungs wehren kann, zumindest wenn der Request als solcher nicht gleich den ganzen Webserver zum Absturz bringt.

Konsequenzen

Im Kapitel über die Security Considerations der W3C-Spezifikation sagen die Autoren, dass Applikationen sich selbst schützen sollen, da sie ja am Origin Header erkennen könnten, woher die Anfrage kommt. Von den getesteten Browsern Firefox, Chrome, Internet Explorer und Safari führt lediglich Safari vor dem Zugriff einen Test mit der »OPTIONS« -Methode durch, um zu prüfen, ob der Webserver den Zugriff von einer fremden Quelle erlauben würde (Abbildungen 1 und 2). In der Praxis bedeutet dies für den Administrator jedoch, dass die Aussage “Unser Webserver ist sicher, denn der ist nur im Intranet erreichbar!” nicht mehr gilt, vor allem dann, wenn der Server einen leicht erratbaren DNS-Namen trägt (siehe Kasten “Nachgefragt”).

Nachgefragt

Das Linux-Magazin hat bei dem Sicherheitsexperten Christian Horchert, Senior Security Consultant und Geschäftsführer der Sektion Eins GmbH nachgefragt, wie er das Problem bewertet und ob er denn Lösungsansätze sieht. Seine Antwort fällt reichlich skeptisch aus:

Linux-Magazin: Kann ich mich als Admin oder Anwender vor dem oben erwähnten Problem schützen?

Christian Horchert: Technisch gesehen sind HTML 5, Web RTC, JSEP, CORS, JSONP und wie sie alle noch heißen eine wahre Katastrophe. Das wird in Zukunft sicher nicht besser, wenn mehr und mehr Anwender anfangen HTTP über UDP zu machen – also das, was Google SPDY nennt. Einer der wenigen und einigermaßen wirksamen Verteidigungsmechanismen ist CSP (Content Security Policy), der zumindest für aktive Inhalte ganz gut funktioniert, offene Ports aber nicht erkennt.

Linux-Magazin: Gibt es bereits Tools, die derartige Fehler ausnutzen?

Christian Horchert: Ja, das ist alles bekannt und gut dokumentiert. Es gibt ein Tool mit Namen Beef [8], das sich einige dieser Eigenschaften aus den Standards zunutze macht. Mit Beef lässt sich beispielsweise XSS auf richtig elegante Weise für Discovery und Angriffe auf lokale Netze ausnutzen. Das gab es auch mal für Flash, dort ist das aber angeblich schon eine ganze Weile gefixt [9].

Das Bedrohungsszenario ist bei all diesen Methoden – wie im Artikel beschrieben – recht einfach: Angreifer können Hosts in lokalen Netzen entdecken, also auch hinter Firewalls oder NAT. Sich dagegen schützen ist nicht trivial.

Abbildung 1: Das Skript hat das lokale Netz vom Firefox-Browser aus gescannt und dabei drei reagierende Hosts gefunden.

Abbildung 1: Das Skript hat das lokale Netz vom Firefox-Browser aus gescannt und dabei drei reagierende Hosts gefunden.

Abbildung 2: Das Frontend sieht in Firefox und Safari zwar gleich aus, doch nur Apples Browser prüft, ob der Webserver fremde Quellen gestattet.

Abbildung 2: Das Frontend sieht in Firefox und Safari zwar gleich aus, doch nur Apples Browser prüft, ob der Webserver fremde Quellen gestattet.

Alle Webapplikationen im LAN um Checks in Sachen Origin Header erweitern, das scheint wohl nur im Elfenbeinturm des W3C realistisch. Wünschenswert wäre es ohne Frage, aber in realen LANs ist es sicher nicht umsetzbar. Dem Problem kommen jedoch mehr und mehr Experten auf die Schliche [5].

Zur Stunde bleibt dem Webserveradmin jedoch nichts anderes übrig, als seine Applikationen und Server so abzusichern, als stünden sie im Internet. Von den Browserherstellern ist zumindest nicht zu erwarten, dass sie das vermeintliche Fehlverhalten ändern.

Wer Lust hat, in dieser Richtung noch weiter zu “spielen”, findet im Blog des Autors noch zwei Beispielskripte ([6] und [7]), um weitere Funktionen auszuprobieren. (mfe)

Für Kommentare

Infos

  1. Andreas Möller, “Direkter Draht”: Linux-Magazin 08/13, S. 92
  2. Same Origin Policy: http://en.wikipedia.org/wiki/Same-origin_policy
  3. OWASP Top Ten: https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project
  4. CORS: http://www.w3.org/TR/cors/
  5. Einar Otto Stangvik, “Local IP discovery with HTML5 WebRTC”: https://2x.io/read/security-by-obscurity
  6. XML-Scan: http://blog.nruns.com/blog/2013/10/07/Risk-of-HTML5-Konstantin/xmlscan.html
  7. Netzwerkscan: http://[http://blog.nruns.com/blog/2013/10/07/Risk-of-HTML5-Konstantin/networkscan.htmhttp://l
  8. Beef: http://blog.beefproject.com/2012/06/beef-in-real-world-pen-test-part-4.html
  9. Flash-Scan: http://scan.flashsec.org
DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 3 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben