3 and a half solutions for Intigriti’s challenge 1220

The rules

The solution to the challenge should meet the following requirements:

Let’s get started!

Navigating to https://challenge-1220.intigriti.io/ we’re met with a simple calculator that can do cool (but simple) calculations.

A leet calculation

Time to check the source code

This seems like a good time to take a look under the hood and examine how the calculator works. The page only uses a single script called script.js (the script can be found at the bottom of this article). Upon the page has loaded, a function called init is called. This function then uses a custom functioncalled getQueryVariable to get the parameters num1 , num2 and operator from the URL. Let’s examine exactly how getQueryVariable retrieves the parameters from the URL.

  1. Split searchQueryString based on ‘&’ chars and save it in the vars array
  2. Split each element in vars based on the ‘=’ char and check if the first part matches the sought GET parameter
  3. If it matches, save the second part in the variable value
  4. Return value
  1. Parameter num1 and num2 are both tested against a regex pattern.¹ The checks are almost equal: Both parameters must only consist of alphanumeric characters except the fact that num1 is also allowed to contain hyphens (‘-’)².
  2. A check is made to make sure the whole operation (all three parameters combined) doesn’t exceed a length of 20.

An initial thought

An interesting thing to note is that the operators array contains the equal operator, which means that we are allowed to set the operator parameter to an equal sign. This is interesting since we’re then able to do assignments, eg. setting the value of a variable.

So what you can set a variable?

Well, it’s not just variables we can assign. In javascript it’s possible to override a function as follows:


Approaching a solution

After discovering that I could override a function, I started to look for functions where I had control over the arguments passed along. I recalled that the function getQueryVariable ran through each GET parameter and passed both their name and value to the function decodeURIComponent. My first idea was to override decodeURIComponent and set it to alert and then have a parameter with either the name or value set to document.domain. However, there was one problem with this approach. To override decodeURIComponent and set it to alert, I would need to have the following operation evaluated:


Try harder

It’s time to try harder. Earlier we managed to pop an alert box with the value of num1 by overriding calc. If only we where able to change the value of num1 before calc is called again we could do something like this:

  1. Set the value of num1 to document.domain
  2. Call calc
Found here

Let’s just not refresh

Remember how I mentioned that getQueryVariable looks at everything after the first occurrence of ‘?’. What if we created a URL fragment…

location.hash = "?num1=document.domain"
A moment of confusion
location.hash = "?&num1=document.domain"

Eval to the rescue

We can simply fix our stupid problem by throwing in an extra eval. If we set calc to eval instead of alert, we can then simply pass along the argument “alert(document.domain)” instead of “document.domain”. Our steps would then be:

  1. Set the value of num1 to alert(document.domain)
  2. Call calc
That’s better

Solution #1

Let’s try and automate our approach. To do so we need to have our own script control the value of the fragment of https://challenge-1220.intigriti.io/. Let’s spicy things up, cross our fingers, and try and load the challenge in an iframe on our own page.

A moment of relief


Let’s finally make some PoC code. Our code should do the following:

  1. Change the fragment to set num1=alert(document.domain)
A beautiful moment

Solution #2 | No user-interaction

But wouldn’t it be nice to have the XSS fire without user-interaction? Yes, it would. So let’s do that!

  1. Set calc=eval
  2. Set num1=alert(document.domain)
Even more beautiful!

Solution #3 | An unintended solution

This was actually the first solution I found. It was an unintended solution and a bit weird, but I thought I would include it. When initially looking at the getQueryVariable function, I thought about the possibility of tricking the function into returning one value for num1 before clicking a button on the calculator and then another value after. Investigating the functions, the clear function seemed interesting. The function was called when clicking the clear button on the calculator, and removed the three GET parameters num1 , num2 and operator. However, unlike getQueryVariable which happily looked for GET parameters in the fragment of the URL, clear only removed the actual GET parameters and not the values specified in the fragment. I really felt like this parsing difference between the two functions should be possible to exploit and after some time, I managed to do so.


Solution #3.5 | A different approach to getting XSS

As a last note, I thought I would cover a slightly different approach which can be incorporated in both solution #1 and solution #2. The idea is to set the location parameter to an XSS payload using the javascript protocol. This can be achieved through the use of the variable searchQueryString which is set to the content proceeding the first occurrence of ‘?’ inlocation.href . However, we run into length problems again:

  1. location=a



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