A new month, a new challenge. Eager for a new Intigriti challenge I was happy to finally see this tweet:
Let’s jump in and read the rules!
- Should work on the latest version of Firefox or Chrome
alert()the following flag:
- Should leverage a cross site scripting vulnerability on this page.
- Shouldn’t be self-XSS or related to MiTM attacks
- Should be reported at go.intigriti.com/submit-solution
At first sight, the page doesn’t seem to have any functionality nor provides us with a cool calculator. But since I crave some Intigriti swag, let’s click the link they’ve kindly provided us with:
Interesting, instead of directly taking us to the swag shop, there’s a custom delay before the page redirects us.
Time to investigate
Let’s dig into the source code to figure out what’s going on! The page loads the script
script.js (you can find the whole script at the bottom of the post)
The first lines seem to check for a GET parameter with the name
r and assign its value to the property
r of the
The next couple of lines look a bit overwhelming, so let’s break the code down into steps:
- Loop through the two strings ‘document’ and ‘window’
- Loop through all the properties for the objects ‘document’ and ‘window’
- Check if the property is of type ‘string’
- If it does, delete the property
This seems like a weird approach to try and secure the application from malicious attacks. Let’s keep this functionality in mind and move on.
These next lines override the default behavior of the anchor links on the page so that the function
safeRedirect will be called with the value of the
href attribute instead of directly redirecting us. This was the behavior we saw when clinking the swag shop link! Then on lines 22–24 a check is made to see if
r exists and if it does, a call is made to
r as a parameter. This looks interesting since we already know that
r is set by using the GET parameter
r! Let’s try to use the GET parameter now to see if we can make a redirect to
Perfect! But what does this
safeRedirect function even do? Time to find out:
First, a check is made to see if the
url parameter provided to the function contains any of the chars:
<>"' (the last being a space). If it does, the function just pops an alert box displaying:
Invalid URL.. Else, a check is performed to see if the parameter begins with the value
https:// if that’s the case,
window.location is set to the value of the parameter. If the parameter doesn’t begin with
https://, a local redirect is attempted by setting
window.origin plus whatever the value of the
url parameter is. Finally, the inner HTML of the tag with the id
popover is set to some markup. What’s worth noting here is that the value of the
url parameter is used intwo places: inside a paragraph tag and as the value of a
href attribute of an anchor tag. You might now think “lmao why don’t we just inject an XSS payload into the
display: none… So even though we’re able to set the
href tag to a working XSS payload, there’s no way for a victim to fire it¹.
The weird filtering functionality seems interesting so let’s take a closer look at that. Which properties of document and window are even available and of type ‘string’? Let’s just quickly modify the code a bit to list them all for us. Something like this will work:
Running our code in the console list all the properties for us, which includes
r as expected. The others don’t look that promising. I guess we can’t influence the origin which will probably always be:
https://challenge-0121.intigriti.io, right? Looking at the policy page for the challenge hints otherwise:
Notice the little ‘*’? We should probably have checked the policy page as the first thing… Anyways, we might be in luck! Let’s try some random subdomain:
window.origin is now undefined!? Oh! The weird filtering stuff must have noticed that the value of the property was
Try harder I said - Or just be lucky
After doing some thorough cool hacking analysis - or maybe just spraying some random payloads at the page - I got something interesting:
Wow, we just escaped the
href attribute! This was totally planned by using this very nice payload I prepared through my research of the challenge *checking URL*:
r=bla%0ahihi. Oh yeah, that’s the payload.² So providing a new-line char in the
href tag in the template string made it possible to create our own attributes for the anchor tag, eg. the
id attribute… This is happening since the no quotes are surrounding the value of the
href attribute which is set to the value of the
url parameter controlled by us.
Wrapping it together - DOM clobbering FTW
So now we can set arbitrary attributes for an anchor tag and have discovered how to remove the
origin property. Could we maybe chain these discoveries together? Of course, we can! What would happen if we set an
id attribute to the anchor tag with the value
origin while we use the subdomain
origin property deleted? The answer is magic. Or maybe just DOM clobbering… Yeah, it’s definitely just DOM clobbering. Let’s try something like
Hype! We just successfully ‘clobbered’ the
origin property of the
window object! So why is this so great you might ask? Well, remember this line of the
Now that we’ve clobbered
href attribute when it’s converted to a string:
While other elements, eg. a paragraph tag would return something like this:
Now we’re finally ready to craft our final payload and pop the alert box!
?r=j%26%23x41;vascript:Use encoding to bypass the filter
Alert the flag
Set the id of the anchor tag to
Let’s try it:
Yes! After patiently waiting 5 seconds, we see that our payload fires! We’ve now successfully solved the challenge!
Thanks for reading along!
- You may have noticed that
displaywill eventually be set to
block, but this will only happen 1 second after the 5 second delay for the redirect. This means that if our browser redirects us to the target location in less than a second, we won’t ever see the change.
- If you didn’t catch my thick sarcastic tone, let me be clear: I discovered this by coincidence. But that’s totally ok. That can be part of the process when doing challenges and even bug bounty hunting.