Cross-Site Scripting im Überblick, Teil 3: Der MySpace-Wurm Samy
Eine persistente XSS-Schwachstelle in einer Webanwendung kann unter Umständen von einem Webwurm zur Verbreitung genutzt werden. Der infizierte dann nicht wie ein herkömmlicher Computerwurm zig verschiedene Server oder Benutzerrechner, sondern die Profile der Benutzer der betroffenen Webanwendung oder ähnliches. Er ist also auf den einen Server mit der betroffenen Webanwendung beschränkt, kann dort aber auch einigen Schaden anrichten.
Der erste, oder zumindest der erste allgemein bekannt gewordene, Web-Wurm war der MySpace-Wurm Samy, der sich am 4. Oktober 2005 auf MySpace ausbreitete.
So funktionierte der MySpace-Wurm
Samy Kamkar war der Meinung, zu wenig Freunde auf MySpace zu haben. Samy (der Wurm) sollte das ändern. Und tat das sehr erfolgreich. Ausgehend von Samy Kamkars Profilseite verbreitete sich der Wurm über eine persistente XSS-Schwachstelle in die Profilseiten der Besucher eines befallenen Profils. Über einen XMLHttpRequest wurde Samy zum Freund und 'Hero' des Besuchers gemacht und der Wurmcode in dessen Profilseite integriert. Innerhalb von 20 Stunden hatte Samy Kamkar mehr als 1 Million Freunde und Samy (der Wurm) entsprechend viele Profile verseucht. MySpace musste den Betrieb vorübergehend komplett einstellen, um den Wurm zu stoppen und alle befallenen Seiten zu reinigen.
Die folgende Beschreibung dey MySpace-Wurms basiert auf Samy Kamkars eigener Beschreibung. Die Methoden, um MySpaces Filter zu umgehen, sehen inzwischen recht altmodisch aus. Der Wurm ist ja auch schon fast 10 Jahre alt.
Bei der Entwicklung musste Samy Kamkar einige Schutzmaßnahmen von MySpace und weitere Einschränkungen überwinden. Was ihm erfolgreich gelang, wie der Erfolg des Wurms eindrucksvoll bewies.
Den Sourcecode des Wurm finden Sie sowohl in der Originalformatierung als auch in einer angepassten, lesbareren Formatierung sowie in einer kommentierten Version mit "sprechenden" Variablennamen hier. Die kommentierte Version sollte, vor allem in Verbindung mit den folgenden Erklärungen, selbsterklärend sein. In der Beschreibung ist das Wort "Code" teilweise mit den entsprechenden Quellen im Sourecode verlinkt.
- MySpace blockierte die meisten HTML-Tags, insbesondere
<script>
-Tags, HREF-Tags mit JavaScript etc.. Nicht gefiltert wurden jedoch CSS-Tags. Samy nutzte aus, das einige Browser wie zum Beispiel der Internet Explorer und einige Safari-Versionen JavaScript-Code in CSS-Tags auswerteten. Dadurch funktionierte der Wurm-Code nicht in jedem Browser, aber es blieben genug potentielle Opfer übrig.
Code:<div style="background:url('javascript:alert(1)')">
- MySpace filterte die Zeichenkette
"javascript"
überall aus - nicht aber"java\nscript"
, was von einigen Browsern als"javascript"
interpretiert wird (\n
ist der Zeilenumbruch, Newline).
Code:<div style="background:url('java
script:alert(1)')">
- Innerhalb des
<div>
-Tags konnten keine Quote-Zeichen verwendet werden, da sowohl'
als auch"
bereits verwendet wurden. Nun ist es ziemlich schwierig, JavaScript ohne Quote-Zeichen zu programmieren. Als Ausweg hat Samy Kamkar den JavaScript-Code in einem Ausdruck gespeichert und dann mit dessen Namen aufgerufen und ausgeführt. Dadurch konnte das einfache Quote-Zeichen ('
) verwendet werden.
Code:<div id="mycode" expr="alert('Hallo!')" style="background:url('java
script:eval(document.all.mycode.expr)')">
Die relevanten Teile sind im unformatierten Sourcecode blau markiert.
- Außer einfacher Quote-Zeichen (
'
) wurden auch doppelte Quote-Zeichen ("
) benötigt. Das escapen (zum Beispielfoo\"bar
) funktionierte nicht, da MySpace alle maskierten Quote-Zeichen ausfilterte. Als Ausweg hat Samy Kamkar die doppelten Quote-Zeichen mit der FunktionString.fromCharCode()
aus ihrem Dezimalcode erzeugt.
Code:<div id="mycode" expr="alert('Doppeltes Quote-Zeichen: ' + String.fromCharCode(34))" style="background:url('java
script:eval(document.all.mycode.expr)')">
- Um den Wurm-Code auf die Profilseite des Opfers zu kopieren
musste auf den Quelltext der Seite zugegriffen werden, um daraus die
ID des Benutzers zu lesen. Eigentlich ginge dies über
document.body.innerHTML
- wenn MySpace nicht die Zeichenkette"innerHTML"
überall ausgefiltert hätte. Als Ausweg wurde"innerHTML"
in einemeval()
-Aufruf aus zwei Strings zusammengesetzt:
Code:alert(eval('document.body.inne' + 'rHTML'));
- Bei der Suche nach der ID des Benutzers,
"friendID"
würde auch der eigene Code gefunden werden, da der diese Zeichenkette ja auch enthält. Daher muss das zu suchende Wort ebenfalls zusammengesetzt werden. Der gleiche Fall tritt auf, wenn der Wurm-Code für die Weiterverbreitung von der aktuellen Seite gelesen wird.
Code:var index = html.indexOf('m' + 'ycode');
- Um die Profilseite des Opfers zu ändern, wurden XMLHttpRequests
verwendet. Das ginge zwar auch ohne, zum Beispiel über versteckte iFrames,
aber der im Hintergrund ablaufende XMLHttpRequest ist deutlich
unauffälliger, außerdem werden die für den Angriff benötigten Cookies
automatisch mitgeliefert. Das für die Durchführung der XMLHttpRequests
notwendige
"onreadystatechange"
wurde von MySpace ausgefiltert und daher ebenfalls in einemeval()
-Aufruf zusammengesetzt.
Code:eval('xmlhttp.onread' + 'ystatechange = callback');
- Um Samy zur Liste der 'Heroes' des Opfers hinzuzufügen, musste
ein POST-XMLHttpRequest an die addFriends-Seite unter
www.myspace.com
geschickt werden. Die aktuelle Seite befand sich jedoch unterprofile.myspace.com
, so dass der Request gegen die Same-Origin Policy verstößt. Um diese Einschränkung zu umgehen, nutzte Samy Kamkar aus, das die Profile sowohl unterprofile.myspace.com
als auch unterwww.myspace.com
erreichbar waren. Die Seite musste also nur unter der gewünschten Domain neu geladen werden.
Code:if (location.hostname == 'profile.myspace.com') document.location = 'http://www.myspace.com' + location.pathname + location.search;
- Um Samy Kamkar zur Freundesliste des Opfers hinzuzufügen, fehlte
nun nur noch ein entsprechender POST-XMLHttpRequest. Um CSRF-Angriffe zu
verhindern, verwendete MySpace in seinen Formularen einen zufälligen
Hash-Wert als Sicherheitstoken, der mit dem zugehörigen POST-Request
abgesendet werden musste. Um diesen Hash-Wert zu erhalten, wurde vor dem
Absenden des XMLHttpRequests eine Seite mit dem Hash-Wert über einen
GET-Request geladen und daraus der Hash-Wert
übernommen.
Denn so ein Token schützt bekanntlich nur vor CSRF-Angriffen, wenn es
nicht ausgespäht werden kann. Weshalb die geschützte Seite eben zum
Beispiel keine XSS-Schwachstelle enthalten darf.
- Um den Wurmcode in die Profilseite des Opfers einzuschleusen, musste dieser erst passend codiert werden. Dazu wurde er von der aktuellen Seite gelesen und manuell passend angepasst. Die JavaScript-Funktionen für die URL-Kodierung und Escape behandelten nicht alle Fälle ausreichend. Weitere Probleme waren zum Beispiel die maximal mögliche Code-Länge, der teilweise Verzicht auf Leerzeichen, das Verbergen von Namen und die Wiederverwendung von Funktionen.
Wie Sie sehen können, besteht der Wurm aus ganz normalen JavaScript-Code, wenn auch teilweise in etwas unüblichen Schreibweisen - und an einer Stelle eingefügt, an der JavaScript eigentlich nichts zu Suchen hat.
Das der Wurm überhaupt funktionierte und sich auf MySpace ausbreiten konnte, hat zwei Gründe:
- Das Hauptproblem sind die unterschiedlichen Interpretationen des HTML- und JavaScript-Codes durch die verschiedenen Webbrowser: Wo der eine ein kaputtes CSS-Tag erkennt und folgerichtig ignoriert, sieht der andere JavaScript-Code, den er ausführt. Samy lief also nicht in allen Browsern, mit dem Internet Explorer aber in einem der verbreitetsten. Ohne, diesen Fehler der Browser wäre der Angriff fehl geschlagen.
- MySpaces Filter gegen XSS-Angriffe war zwar sehr gut, aber eben nicht
gut genug war. Das Problem war einfach, das einige HTML-Tags zugelassen
waren und andere nur auf eine Weise ausgefiltert wurden. Was wieder einmal
den Nachteil von Blacklists zeigt: Was nicht drauf steht, wird
durchgelassen. Wenn z.B. wie in diesem Fall
'innerHTML'
verboten ist, hätte auch nach'i'+'nnerHTML'
,'in'+'nerHTML'
, ...'i'+'n'+'nerHTML'
, ... usw. usf. gesucht werden müssen. Und alle möglichen anderen Kodierungen.
In der nächsten Folge geht es um eine weiter Art von XSS-Angriffe: DOM-basiertes XSS.
Übersicht über alle Artikel zum Thema
- Cross-Site Scripting im Überblick, Teil 1: Reflektiertes XSS
- Cross-Site Scripting im Überblick, Teil 2: Persistentes XSS
- Cross-Site Scripting im Überblick, Teil 3: Der MySpace-Wurm Samy
- Angriffe über Cross-Site Scripting: Der Sourcecode des MySpace-Wurms Samy
- Cross-Site Scripting im Überblick, Teil 4: DOM-basiertes XSS
- Cross-Site Scripting im Überblick, Teil 5: Resident XSS
- XSS-Angriffe, Teil 1: Informationen einschleusen
- XSS-Angriffe, Teil 2: Cookies und Tastendrücke ausspähen
- XSS-Angriffe, Teil 3: Zugangsdaten ausspähen
- XSS-Angriffe, Teil 4: Ein Blick in die History, und dann auf ins LAN!
- XSS-Angriffe, Teil 5: Ein Portscan (nicht nur) im LAN
- XSS-Angriffe, Teil 6: Ein verbesserter Portscanner
- XSS-Angriffe, Teil 7: Hindernisse beim JavaScript-Portscan beseitigen
- XSS-Angriffe, Teil 8: Ein Portscan mit WebSockets oder Cross-Origin Requests
- XSS-Angriffe, Teil 9: Der Router im Visier
- XSS-Angriffe, Teil 10: Weitere Angriffe auf den Router
- XSS-Angriffe, Teil 11: Unerwünschtes Firmware-Update für den Router
- XSS-Angriffe, Teil 12: Browser-basierte Botnets
- XSS-Angriffe, Teil 13: Fortgeschrittene Angriffe
- XSS-Angriffe, Teil 14: Das Browser Exploitation Framework BeEF
Trackbacks
Dipl.-Inform. Carsten Eilers am : Drucksache: PHP Magazin 4.2015 - Cross-Side Request Forgery
Vorschau anzeigen
Dipl.-Inform. Carsten Eilers am : Unerfreuliches zu Routern und dem IoT
Vorschau anzeigen
Dipl.-Inform. Carsten Eilers am : Neues eBook: "Websecurity - Angriffe mit SSRF, CSRF und XML"
Vorschau anzeigen
Dipl.-Inform. Carsten Eilers am : Die IoT Top 10, #1: Unsichere Weboberflächen, Teil 5
Vorschau anzeigen
Dipl.-Inform. Carsten Eilers am : Drucksache: PHP Magazin 6.18 - OWASP Top 10, Platz 7 und 8 - "Cross-Site Scripting" und "Insecure Deserialization"
Vorschau anzeigen