XSS Challenge - 10K Followers Intigriti

25/11/2019

Here's a brief explanation of my solution for the 10K Followers Intigriti's XSS Challenge.


The challenge consisted in finding a XSS vulnerability on the following code which was hosted on https://challenge.intigriti.io/.

<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>

What this code should do is just grabbing the hash element of the URL, check if it's a whitelisted domain, then write it on the document and redirect the user there. If it's not whitelisted, then this is written on the document and nothing else happens.

After not reading the code correctly, I thought the if statement was just checking if the hostname contained intigriti.com or intigriti.io. So my first reaction was to get a domain name that matched.

But I reread the code and saw the hostname had to be one of those exactly, so no domains were needed, only intigriti.com or intigriti.io could be used.

Someone could think that the vulnerable part is when document.write is used because we could directly write something like <script>alert()</script> on the document, but here there should be no problem in using it, since on line 5 the function encodeURIComponent is used encoding all special characters, and on line 11 only a hostname could be written, and a hostname can't contain characters like <.

The only insertion point that we have left is on line 7 where the redirection is performed. Something that everyone may not know is that is possible to use a redirection like this to execute javascript code using a value like location = 'javascript:alert()';.

Finally, the trick on the code is the double usage of location.hash.substr(1) on line 3 (before the if) and on line 7 (after the if). Between these two instructions is possible to change the hash to bypass the if statement and this can be done with an iframe in a script like the following (you have to play with the setTimeout value to perform the hash change on the exact moment).

<html>
<body>
<script>
  var iframe = document.createElement('iframe');
  iframe.src = 'https://challenge.intigriti.io/#http://intigriti.com/';
  document.body.appendChild(iframe);
  setTimeout(function(){
    iframe.src = 'https://challenge.intigriti.io/#javascript:alert(location.host)';
  }, 200);
</script>
</body>
</html>