Solution for Intigriti’s 0121 challenge

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!

The solution…

- Should work on the latest version of Firefox or Chrome
- Should
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 (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 and assign its value to the property of the object.

The next couple of lines look a bit overwhelming, so let’s break the code down into steps:

  1. Loop through the two strings ‘document’ and ‘window’
  2. Loop through all the properties for the objects ‘document’ and ‘window’
  3. Check if the property is of type ‘string’
  4. If it is, check if it contains the substring ‘javascript’
  5. 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 will be called with the value of the 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 exists and if it does, a call is made to with as a parameter. This looks interesting since we already know that is set by using the GET parameter ! Let’s try to use the GET parameter now to see if we can make a redirect to :

https://challenge-0121.intigriti.io/?r=https://example.com

Perfect! But what does this function even do? Time to find out:

First, a check is made to see if the 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: . Else, a check is performed to see if the parameter begins with the value if that’s the case, is set to the value of the parameter. If the parameter doesn’t begin with , a local redirect is attempted by setting to plus whatever the value of the parameter is. Finally, the inner HTML of the tag with the id is set to some markup. What’s worth noting here is that the value of the parameter is used intwo places: inside a paragraph tag and as the value of a attribute of an anchor tag. You might now think “lmao why don’t we just inject an XSS payload into the tag?” Well, while we can rather easily bypass the weird filtering approach which removes occurrences of the string ‘javascript’, by simply providing a payload using encoding such as , we have a problem. The anchor tag is inside a paragraph tag with the style … So even though we’re able to set the tag to a working XSS payload, there’s no way for a victim to fire it¹.

Try harder

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 as expected. The others don’t look that promising. I guess we can’t influence the origin which will probably always be: , 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:

Looks like any subdomain will just point towards the challenge page. Well, what will happen if we use ‘javascript’ as a subdomain…

is now undefined!? Oh! The weird filtering stuff must have noticed that the value of the property was which contains the substring ‘javascript’ which then made it delete the property! Not sure how this could help us, but it seems interesting so let’s note it down before moving on.

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:

https://challenge-0121.intigriti.io/?r=bla%0ahihi

Wow, we just escaped the attribute! This was totally planned by using this very nice payload I prepared through my research of the challenge *checking URL*: . Oh yeah, that’s the payload.² So providing a new-line char in the tag in the template string made it possible to create our own attributes for the anchor tag, eg. the attribute… This is happening since the no quotes are surrounding the value of the attribute which is set to the value of the 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 property. Could we maybe chain these discoveries together? Of course, we can! What would happen if we set an attribute to the anchor tag with the value while we use the subdomain to have the 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 property of the object! So why is this so great you might ask? Well, remember this line of the function:

Now that we’ve clobbered we can use the JavaScript protocol to execute JavaScript! This works since the anchor tag is will return the value of its attribute when it’s converted to a string:

While other elements, eg. a paragraph tag would return something like this:

Payload time

Now we’re finally ready to craft our final payload and pop the alert box!


  1. Use the ‘javascript’ subdomain to delete
  2. Use encoding to bypass the filter

  3. Alert the flag

  4. Set the id of the anchor tag to to clobber

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!

@holme_sec

  1. You may have noticed that will eventually be set to , 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.
  2. 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.

Content of :

Bug Bounty Hunter

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store