Intigriti 1021 - XSS Challenge Writeup
Breno Vitório
Posted on November 1, 2021
Halloween came with an awesome XSS Challenge by Intigriti, and I'm here to present the solution I found for this. Hope you like it 🦇
🕵️ In-Depth Analysis
Reading the content of the page, at the first glance, it tells us that there is a query parameter called html
, which is capable of define partially what's displayed to the user. When we define, for example, a <h1>
tag to this parameter, we are going to get returned a page with this tag being reflected, which is already an HTML injection. From now on, we will be working to make it become an XSS.
🙈 Oops, CSP
If we simply try to inject something like <script>alert(document.domain);</script>
, this script tag will be reflected, but the code itself will not be executed. Why? Well, if we look at the head of the page, we are going to find something interesting:
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-eval' 'strict-dynamic' 'nonce-random'; style-src 'nonce-random'">
This meta tag tells us that the page has a CSP, which will not let any random script be executed. Also, it's possible to see, from the script-src policies, that 'strict-dynamic' was defined, which means that generally a script will only be trusted if it comes with a trusted one-use token (nonce).
But there is an exception to the strict-dynamic rule. It allows JavaScript code to be executed if it's being created by using the function document.createElement("script")
, and by the way, if we look a little bit further at the page source, we are going to find this section of code:
🚧 Managing to work with the DOM
When we don't pay enough attention to the code, we might think that it's just needed to insert something like alert(document.domain)
to the xss
parameter on the URL, but if you do so, you won't get any alert popping out, because what's truthfully being inserted to the script tag is: )]}'alert(document.domain)
. Something like that will never be executed, because it returns an error from JavaScript right on the first character.
Paying a little bit more attention to the previous section of code, this specific piece is important:
Now, we know that we have to create a tag with an id "intigriti", and also that this tag needs to, somehow, unbreak the )]}'
that we have seen. The second part its actually pretty easy to think of, because it ends with a simple quotation mark, and if we open it before, every other character will be considered part of the string, so the solution for this would be something like a='
, but we have to apply this on the context of an HTML tag, resulting in <div><diva='>
. Remember that Intigriti Jr's INTERNAL HTML is what is parsed, and not the element itself, that's the reason for the external div.
The other part is the one who takes more effort. If we simply try to add <div id="intigriti"><div><diva='></diva='></div></div>
to the html
parameter, as you can see on the picture below, we will have these tags inside of the DOM but inside <div>
and <h1>
tags, and waaaay too far from being the last element of the body, which is what is want:
So, in order to trigger an alert, we have to figure out a way of go outside this <div><h1></h1></div>
pair and a way of making the next divs fit inside our payload <div id="intigriti"><div><diva='></diva='></div></div>
. One possibility is to trick the browser by inserting unopened/unclosed tags, so it tries and fails to fix it.
🏁 Getting there
For getting outside of the <div><h1></h1></div>
pair, all we have to do is insert </h1></div>
before our friends <div id="intigriti">
, <div>
and <diva='>
, resulting in:
Now we have to make everything that originally goes next </h1></div><div id="intigriti"><div><diva='></diva='></div></div>
, fit inside our structure so it becomes the last element of the body. Just by leaving the DIVs unclosed, like </h1></div><div id="intigriti"><div><diva='>
, we will have as result that all the divs that goes after our payload instantly fit inside <div id="intigriti">
, which is great but not our final goal.
Finally, by adding a <div>
tag and leaving it unclosed at the end of our payload, everything will fit inside our <diva='></diva='>
tags, and also, if we look at the generated script tag, we will find something REALLY insteresting:
<script type="text/javascript">a= '>)]}' null</script>
This means that all the weird characters were turned into a string called "a", and we just have to insert our alert onto the xss
parameter. This would result on the final payload:
https://challenge-1021.intigriti.io/challenge/challenge.php?html=</h1></div><div id=intigriti><div><diva='><div>&xss=;alert(document.domain)
And from this payload right down below, I was able to trick our fictional villain 1337Witch69 🤗
Thank you for taking your time 🤗
Posted on November 1, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.