Intigriti - XSS Challenge 1121
Before everything, I should say that it was a very hard Challenge, I spent many hours but I managed to solve it, here we go!
Summary
The XSS challenge hosted by Intigirti occurs once a month, you can follow them on Twitter to stay tuned about upcoming challenges and other nice infosec contents, I really recommend you to try those challenges as it is a good source for learning and they are always very challenging.
The challenge created by @IvarsVids was available on https://challenge-1121.intigriti.io/
challenge instructions page |
Context
As usual, in the XSS challenge we need to find a way to execute the XSS alert(document.domain). This time the scenario was a bit different, there was a Vuejs page with php extension which receives text input from users in the challenge page.
challenge page |
Reconnaissance
The Challenge page receives a request parameter called s with the input value, and it results in the vuejs.php page being loaded which forwards some redirects, let's check it better
http requests list |
Assessing the implementation we can see some important aspects.
In a loading sequence we have:
Line 3: The user input is stored in the document in the <title> tag, in this way, we can close the </title> tag and insert new html tags from the user input.
Line 5: A variable called isProd is created and set to True.
Line 46: a variable called delimiters is set with an array value which seems to be the double mustache Vue interpolation format.
Line 47: the method AddJS() is called receiving a string that seems to be the url path loaded, and another argument called initVUE, looking at those parameters they seem obvious, let's continue.
document - line 46, line 47 |
Line 8-14: The method is creating a <script> element to load the ./vuejs.php and calling initVUE in the onload event.
document, line 8, addJS function |
Line 16-42: the initVUE() method fill the data using Vue js.
document, line 16-42, initVUE function |
Line 48-63: last but not least, a piece code never being executed (the variable isProd is set to true) on the line 50, something to be explored.
document, line 48-63, code not executed |
Exploitation
csp nonce |
At this stage, I had to find a way to change this variable isProd, but how? In the meantime the first Intigriti hint was released on Twitter
💡 100 likes already? Time for a hint!
— INTIGRITI (@intigriti) November 15, 2021
Sometimes, you need to make your enemy stronger in order to be able to break it! That's just our policy! pic.twitter.com/jgbNL8fJn5
I realized that keeping an opened <script> tag was wrapping the script block which sets the variable isProd and then the variable was not set thanks to CSP!! So, in my mind, the enemy was the CSP and it got stronger blocking my payload...
wrapping script tag with another script tag |
Let's move to the part 2, now we can exercise the code as of line 50:
inside the not executed code |
On the second condition we have the solution for the part 2, this is the else if (version === 999999999999)) but there is a problem here...
The condition expects that version is greater than 1 trillion (13 digits), but version variable can have only 12 digits and it is a String being compared to a Number, how to evaluate it to true?
To answer that we need to understand Javascript, more details can be found here, but to summarize, the javascript will convert the string to a number when performing the comparison.
Now, the string should have a value bigger than 1 trillion but the variable can receive only 12 digits.
To achieve the condition, we can declare the numeric string in a different way, we can use hexadecimal format or exponential format, more details here.
version bigger than 1 trillion |
Due to that, we have now the ability to create a new <script> tag in the document but what we should do with that?
I took a lot of time thinking about this vueDevtools variable because of the regex being applied on it.
💡 A lot of people have been requesting this hint!
— INTIGRITI (@intigriti) November 19, 2021
Here it goes: Is that regex blocking you? Well it was created to be secure and only allow one script... Maybe you need to look in a different direction! pic.twitter.com/WnDIOg0qGi
I GOT STUCK HERE, FOR DAYS!!
During those days, I found that the only way to call the alert(document.domain) would be by Vue, searching on the internet I found this payload from portswigger: v-{{_c.constructor(%27alert(document.domain)%27)()}} but due to the <script> tag created in stage 1, I was not being able to make ./vuejs.php be executed in the page.
💡 100 likes already? Time for a hint!
— INTIGRITI (@intigriti) November 15, 2021
Sometimes, you need to make your enemy stronger in order to be able to break it! That's just our policy! pic.twitter.com/jgbNL8fJn5
Solution
My solution was a bit strange, I could do it simpler but I liked how I did it and I don't want to make it simpler.
During the test, I fould that I could use parameter polution on the search parameter (s).
?s=v-{{_c.constructor(%27alert(document.domain)%27)()}}
&s=</title><meta http-equiv="Content-Security-Policy" content="script-src>
A lot of things here, let me explain that.
From Vue side, the first s value is taken, but the second is loaded in the page. When the second payload containing the <meta> tag payload is loaded, it is messing the html and the declarion from variable isProd is moved out from <script> tag.
after meta tag insertion |
And thanks to this change, the XSS is triggered:
the glory |
The final payload:
https://challenge-1121.intigriti.io/challenge/index.php?s=v-{{_c.constructor('alert(document.domain)')()}}&version=9e9999&s=</title><meta http-equiv="Content-Security-Policy" content="script-src>&vueDevtools=.. /challenge/vuejs.php
Thanks
Extra
Payload:
https://challenge-1121.intigriti.io/challenge/index.php?s=%27v-{{_c.constructor(%27alert(document.domain)%27)()}}%3C/title%3E%3Cscript%3E&version=9e9999&vueDevtools=./vuejs.php
Comments
Post a Comment