Inhalt
Einleitung
Cross-Site Scripting, kurz “XSS”, ist eine Web-Sicherheitslücke, die es einem Angreifer ermöglicht, die Interaktionen mit der Web-App zu manipulieren und zu gefährden. Durch XSS kann ein Angreifer die Same-Origin Policy umgehen, die normalerweise verschiedene Webseiten voneinander trennt. Wenn ein Angreifer erfolgreich eine XSS-Attacke ausnutzt, kann er sich als das Opfer ausgeben und Aktionen im Namen des Opfers ausführen. Der Angreifer erlangt Zugriff auf sensible Daten des Opfers und wenn das Opfer ein Admin oder ein anderer hoch privilegierter Nutzer ist, kann der Angreifer sogar die gesamten Funktionen der Web-App übernehmen. Daher ist es wichtig, XSS-Schwachstellen frühzeitig zu erkennen und zu beseitigen, um die Sicherheit der Webanwendung zu gewährleisten.
Erklärung
Das Ziel einer Cross-Site-Scripting-Attacke besteht darin, schädlichen JavaScript-Code auszuführen. Hierfür manipuliert der Angreifer die angegriffene Website, um den Nutzer dazu zu bringen, den schädlichen Code über den Browser auszuführen. Wenn der Angreifer erfolgreich die Schwachstelle ausnutzt, erhält er vollen Zugriff auf die Interaktionen des Nutzers mit der Applikation.
Um eine Cross-Site-Scripting-Schwachstelle zu finden und zu bestätigen, werden bestimmte Payloads wie beispielsweise <script>print(test)</script>
verwendet. Dabei ist es wichtig zu beachten, mit welchem Browser die Schwachstellen getestet werden, da jeder Browser eigene Sicherheitsmaßnahmen hat. Zum Beispiel verbietet Chrome die Ausführung der “alert()” Funktion durch Cross-Origin iFrames.
Cross-Site-Scripting wird in verschiedene Typen unterteilt, nämlich Reflected XSS, Stored XSS und DOM-based XSS.
Reflected XSS
Die einfachste Form des Cross-Site-Scripting ist das Reflected XSS. Reflected XSS tritt bei Webanwendungen auf, die Daten in einer HTTP-Anfrage akzeptieren und diese Daten direkt in der Antwort zurückgeben, ohne sie zu filtern.
https://9bears-insecure.com/error?err_message=error+in+nachricht.<script>alert(1)</script>
Wenn ein Nutzer diese URL besucht, wird der eingefügte JavaScript-Code im Browser des Nutzers ausgeführt, mit den Rechten der aktuellen Nutzer-Session. Je nachdem, welche Payload der Angreifer wählt, kann er Daten extrahieren und Aktionen ausführen, auf die der Nutzer berechtigt ist.
Stored XSS (persistent XSS oder Second Order XSS)
Stored XSS tritt bei Webanwendungen auf, die Daten von unsicheren Quellen empfangen und diese Daten zu einem späteren Zeitpunkt in einer HTTP-Antwort wiedergeben. Solche Daten können beispielsweise durch Kommentare auf einem Blog, Benutzernamen oder Kontaktinformationen auf einer Profilseite eines Benutzers übermittelt werden.
DOM-based XSS
DOM-based Cross-Site-Scripting tritt in Webanwendungen auf, die clientseitiges JavaScript enthalten und Daten von unsicheren Quellen verarbeiten, die nicht gefiltert werden. Hier ist ein Beispiel für eine solche clientseitige JavaScript-Codezeile:
var search = document.getElementById('search').value;
var results = document.getElementById('results');
results.innerHTML = 'You searched for: ' + search;
Dieses Code-Snippet liest die Daten aus einem Eingabefeld und schreibt sie in ein Element innerhalb des HTML-Codes.
Wenn ein Angreifer die Eingabe kontrolliert und diese nicht ausreichend überprüft wird, kann er schädlichen JavaScript-Code einschleusen und ausführen.
Ausmaß eines cross-site Scripting Angriffes
Eine erfolgreiche Ausnutzung einer Cross-Site-Scripting-Schwachstelle kann dazu führen, dass der Angreifer folgende Dinge bewirken kann:
- Sich als der aktuelle Nutzer ausgeben und Aktionen im Namen des Nutzers ausführen
- Daten auslesen, auf die der Nutzer Zugriff hat
- Auslesen der gespeicherten Passwörter im Browser
- Umstrukturierung und Zerstörung des HTML-Codes
- Einbetten von Trojanern in die Website
Proof of Concept
Im Folgenden finden Sie Beispiele für die jeweils angesprochenen Arten von Cross-Site-Scripting.
Reflected XSS
Im folgenden Beispiel haben wir eine Webfunktion, die ein Eingabefeld zur Eingabe unseres Namens bereitstellt. Sobald wir unseren Namen eingeben und auf “Submit” klicken, wird ein Text ausgegeben, der “Hallo” und unseren Namen enthält. Dies ist ein typischer Angriffsvektor für Reflected XSS, da die Nutzereingabe im HTML-Code zurückgegeben wird.
Um zu prüfen, ob eine XSS-Schwachstelle besteht, geben wir zunächst einen einfachen HTML-Code als Eingabe ein, wie beispielsweise <h1>XSS</h1>
, und klicken dann auf “Submit”.
Wie man sehen kann, wurde der HTML-Code übergeben und interpretiert. Nachdem wir also wissen, dass XSS möglich ist, können wir weitere Tests durchführen und beispielsweise versuchen, die print()-Funktion aufzurufen. Hierfür geben wir folgenden Code als Eingabe ein: <script>print()</script>
Wie wir sehen, öffnet sich sofort nach dem Absenden der Payload ein Druck-Fenster. Somit haben wir bewiesen, dass XSS möglich ist. Welche Möglichkeiten sich durch XSS eröffnen, jenseits von einfachen Fenster-Prints und Alert-Fenstern, werde ich Ihnen zeigen, nachdem ich alle Arten von XSS einmal demonstriert habe.
Stored XSS
Als nächstes betrachten wir eine einfache Stored XSS. Eine Stored XSS speichert die Eingaben eines Nutzers in einem Nachrichtenfeld, wie beispielsweise einem Kommentarfeed unter einem Produkt oder einem Gästebucheintrag, und gibt sie bei erneutem Besuch der Seite wieder aus, wie im folgenden Beispiel.
Wir gehen hier genauso vor wie bei einer Reflected XSS. Wir haben ein Eingabefeld für unseren Namen und ein weiteres für eine Nachricht. Wir geben beispielsweise HTML-Code wie <h1>Test</h1>
ein und klicken auf “Submit”, um zu sehen, ob unser Code im HTML erscheint und interpretiert wird.
Danach können wir andere XSS-Payloads ausprobieren und experimentieren. Stored XSS ist kritischer als Reflected XSS, da es den Payload speichert und bei zukünftigen Besuchen der Seite automatisch ausführt, wodurch mehrere Nutzer gleichzeitig angegriffen werden können, ohne dass der Angreifer ein bestimmtes Ziel hat. Im Gegensatz dazu kann man bei Reflected XSS gezielt Nutzer durch Phishing-Methoden angreifen, indem man ihnen einen Link sendet.
DOM-Based XSS
Im Folgenden sehen wir einen Code-Schnipsel einer Webanwendung, die uns die Sprache wählen lässt. Wir können eine voreingestellte Sprache auswählen oder eine eigene Sprache festlegen, indem wir den Link abändern, beispielsweise durch ?default=unsere_sprache.
if (document.location.href.indexOf("default=") >= 0) {var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");document.write("<option value='' disabled='disabled'>----</option>");}document.write("<option value='English'>English</option>");document.write("<option value='French'>French</option>");document.write("<option value='Spanish'>Spanish</option>");document.write("<option value='German'>German</option>");
Was uns besonders interessiert, ist der Teil des Codes, in dem die Variable lang erstellt wird.
{var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
Anhand dieses Code-Schnipsels sehen wir, dass die Variable lang erstellt wird und das eingesetzt wird, was wir hinter ?default= schreiben. Hier können wir ganz einfach unseren HTML- oder JavaScript-Code einfügen und testen, wie bei den oben genannten Beispielen.
Was kann alles passieren bei erfolgreicher XSS?
Im Folgenden werde ich verschiedene Möglichkeiten aufzeigen, wie XSS-Schwachstellen ausgenutzt werden können, um zu zeigen, dass XSS-Angriffe nicht nur einen einfachen Alert auslösen, sondern eine kritische Schwachstelle darstellen.
Cookies stehlen und Account Übernahme
Wenn eine XSS-Schwachstelle erfolgreich ausgenutzt wird und weitere Sicherheitsmaßnahmen nicht vorhanden sind, kann ein Angreifer den Session-Cookie stehlen, den ein Nutzer nach dem Login erhält. Dies ist jedoch nur möglich, wenn keine anderen Sicherheitsmaßnahmen wie z. B. SameSite oder HttpOnly aktiviert sind.
Ein Angreifer kann dies einfach erreichen, indem er den Cookie per JavaScript-Code ausgibt.
Im obigen Fall handelt es sich um den security=low-Cookie und den PHPSESSID. Wenn wir diesen Cookie haben, können wir ihn in unserem Browser hinterlegen und uns als der Nutzer einloggen, von dem wir den Cookie gestohlen haben.
Passwortänderung mit XSS und CSRF
Wenn neben der XSS-Schwachstelle noch andere Sicherheitslücken vorhanden sind, kann ein Angreifer auch Passwörter ändern oder die E-Mail-Adresse eines Nutzers manipulieren.
Im folgenden Beispiel haben wir eine Webanwendung mit einer Stored XSS-Schwachstelle und einer CSRF-Schwachstelle.
Um eine Passwortänderung des Nutzers zu erzwingen, muss man zunächst herausfinden, wie der Link für die Passwortänderung aufgebaut ist. In unserem Beispiel suchen wir nach der CSRF-Schwachstelle und finden dort ein Formular zur Passwortänderung. Wir geben eine neue Passwort-Kombination ein und klicken auf “Change”. Dadurch wird uns folgender Link in der URL-Leiste angezeigt:
192.168.178.83/DVWA/vulnerabilities/csrf/?password_new=test1234&password_conf=test1234&Change=Change
Wir kopieren diesen Link und fügen ihn in einen Kommentar ein, der die XSS-Schwachstelle enthält, beispielsweise in einem Gästebuch oder Kommentarbereich.
Abwehr von XSS Schwachstellen
Um sich vor XSS-Attacken zu schützen, sollten folgende Maßnahmen beachtet werden:
- Eingaben filtern: Filtern Sie die Nutzereingaben je nach erwartetem Eingabetyp und den erlaubten Eingaben.
- Daten encoden: Sorgen Sie dafür, dass die Nutzerausgabe, die über HTTP-Response ausgegeben wird, encodiert wird, um zu verhindern, dass sie als gültiger Code interpretiert wird.
- Content Security Policy: Verwenden Sie die Content Security Policy, um das Ausmaß weiterer XSS-Angriffe zu reduzieren.
- Setzen von HTTP-Response-Headern: Bei HTTP-Responses, die kein HTML oder JavaScript enthalten sollten, sollten Sie den Content-Type und X-Content-Type-Options Header setzen, um sicherzustellen, dass der Browser die Response so interpretiert, wie es gewollt ist.