Introduction

This was an XSS challenge hosted by Intigriti whose original creator was @PiyushThePal. You can find it here.

Source Code review

The first thing I do while checking for XSS is just press CTRL+U on the page and quickly skim through the interesting-looking JS files to find something that might stand out, like the use of innerHTML or using dangerous functions like eval etc. In the view source page, I found a js file that was being referenced from a githack page (somehow, now they have changed the view source page and now they have explicitly added the code that was in that file earlier, I was lucky to have tried to solve the challenge before this was edited). So, I searched for query-plugin-query-object which was the file name of this file and I found a repository that had the exact same code (almost). I figured out that they have made the challenge around this code and omitted some code to create a vulnerable code. So, I diff the 2 files:

132,137c132,135
<         if(!key.includes("__proto__")){
<           var value = !is(val) ? null : val;
<           var parsed = parse(key), base = parsed[0], tokens = parsed[1];
<           var target = this.keys[base];
<           this.keys[base] = set(target, tokens.slice(0), value);
<         }
---
>         var value = !is(val) ? null : val;
>         var parsed = parse(key), base = parsed[0], tokens = parsed[1];
>         var target = this.keys[base];
>         this.keys[base] = set(target, tokens.slice(0), value);

Clearly, as you can see they have omitted the __proto__ check from the original code. This means this has to be prototype pollution leading to XSS. Now, I am not good at Prototype pollution (or at anything for that matter, lol). So, I started looking for some resources on the internet to get some knowledge about Prototype pollution until I found a youtube video by Intigriti’s PinkDraconian on a previous XSS challenge that too required exploiting prototype pollution leading to XSS. You can find that here. After watching this video, I found a goldmine for prototype pollution. This is a repository by BlackFan that has a lot of prototype pollution scenarios and payloads for them 🤯. And, fortunately, it has what we need too.

Finding the payload

While having a look at the source code, I found where our input, that is page param, is being stored.

  var pl = $.query.get('page');
  if(pages[pl] != undefined){
    console.log(pages);
    document.getElementById("root").innerHTML = pages['4']+filterXSS(pages[pl]);
  }else{
    document.location.search = "?page=1"
  }

I thought this $.query.get('page') is vulnerable to prototype pollution and there were some scenarios similar to this in that repository too, so I started applying those payloads but nothing was working. After some time, I realised some characters cause an error like %.

e.g.: ?page=meispi%

error

I tried to look for these errors but found nothing interesting and got bored. After some time, I looked at the repo again and found a scenario that was based on the use of js-xss and our code too made the use of this library. Notice the filterXSS function, that is the vulnerable part.

So, reading the repo, I figured out that our payload needs to be something like: ?__proto__[whiteList][img][0]=onerror&__proto__[whiteList][img][1]=src

Before adding the whiteList variable:

filterXSS-bef

After adding the whiteList variable:

filterXSS-after

Ok, now how do we reach the if part to load our payload through innerHTML. Somehow, we need to enter our payload in pages[pl]. But pages is already defined and I tried to overwrite it through the URL using __proto__ but it was again overwritten by the content when I reload the page. I tried to analyze the key-value pairs of the pages dictionary, but I could find nothing.

Inspect element to the rescue!

Remember our incomplete payload, I just put that in the url and opened inspect element to see what has changed, ?__proto__[whiteList][img][0]=onerror&__proto__[whiteList][img][1]=src&page=1

pages-pp

Ah!!! Our payload becomes a property of the pages object, so we simply need to add another property to the pages object and call that using page param.

Final payload: ?__proto__[whiteList][img][0]=onerror&__proto__[whiteList][img][1]=src&__proto__[x]=<img%20src%3dx%20onerror%3dalert(document.domain)>&page=x

Works on both chrome and firefox!

chrome-solved

firefox-solved