DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 967,611 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Intigriti 1021 - XSS Challenge Writeup
Breno VitΓ³rio
Breno VitΓ³rio

Posted on

Intigriti 1021 - XSS Challenge Writeup

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.

Page with bats flying around and an HTML Injection

πŸ™ˆ 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:

Script tag containing the create element function, and a new request parameter called xss

🚧 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:

Section of code which defines how the script tag should be parsed

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:

HTML content with our injected tags

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:

HTML content with our payload going outside of h1 and div

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 πŸ€—

Happy Ending

Thank you for taking your time πŸ€—

Top comments (0)

Visualizing Promises and Async/Await 🀯

async await

☝️ Check out this all-time classic DEV post