Hey all,
On March 13th I was doing some boring college assignments then I opened Twitter to check what’s new, then I saw that Intigriti was hosting “Easter XSS Challenge” I said let’s give it a shot.
Here is a screenshot of the main challenge page:
The rules are clear so we don’t need to explain them.
First Try
First, I checked the page source code:
Nothing interesting in the HTML code but this script (obviously duh!).
The script.js file contains the following JS code:
var hash = document.location.hash.substr(1);
if(hash){
displayReason(hash);
}
document.getElementById("reasons").onchange = function(e){
if(e.target.value != "")
displayReason(e.target.value);
}
function reasonLoaded () {
var reason = document.getElementById("reason");
reason.innerHTML = unescape(this.responseText);
}
function displayReason(reason){
window.location.hash = reason;
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", reasonLoaded);
xhr.open("GET",`./reasons/${reason}.txt`);
xhr.send();
}
What this code does in steps:
1- It checks if the hash property is set in the location object.
2- Then it sends an XHR request to the selected value (file).
3- It reads and prints the response to the page.
The line 11 is the solution to this XSS challenge, wait, is it that easy?
The page sets the innerHTML of the div
to a value that has been through unescape()
so URL-encoding could be bypassed.
Let’s do it and send a request to 404 page. I wrote https://challenge.intigriti.io/#test
and clicked enter:
Our filename was reflected in the response so we only need to write <img src=x onerror=alert(1)>
as the filename and things gonna work out!
As I expected it is not that easy! the 404 page encode the filename with percentage-encoding and removes % char from the response!
I tried to play with the 404 page and insert some random texts and chars but to no avail, it seems like a dead-end.
Second Round
I fingerprinted the server to check if it is a server related issue, the HTTP response header included server: Google Frontend
but that isn’t the real HTTP server this is a google app engine response header. I just did it manually since Nmap
will give the same output server: Google Frontend
.
I send a very long filename in order to trigger a 4xx error and the result was:
So this is an Apache server we need to trigger an error page that can respond with the URL, hmmm.
We only have control over 4xx pages since we can’t see 404 we need a 403 error page.
We can get a 403 page if we tried to access .htaccess
or server-status
, but the content of the URL is escaped so we gonna see HTML-entities.
We can write our payload as percentage encoding which is accepted and will be written as it is.
We have to double encode <img src=x onerror=alert(1)>
since we are sending it from another page. After encoding it will be %253Cimg%2520src%253Dx%2520onerror%253Dalert(1)%253E
.
We need to be in the website root so we need ../server-status?%253Cimg%2520src%253Dx%2520onerror%253Dalert(1)%253E
Well, it works! but I forgot the CSP because it was too small and I didn’t notice it :D
content-security-policy: default-src 'self'
After I saw this I dropped all the XSS payloads in the bin because we need to insert a script from inside this origin!
Furthermore, <script>
tag can’t be executed using innerHTML
, why? -> reason
So here is one of the tricks to executing <script>
tag with innerHTML
using <iframe>
with the srcdoc attribute which specifies the HTML content of the page to show in the inline frame. But the biggest question from where we can upload script to this host! that’s impossible.
Last Round
We can’t use the 403 as a script since it is not valid JS. But wait! what about the custom 404 page! it seems legit JS.
404 - 'File "test" was not found in this folder.'
We have control over test
string, we need to make it valid JS. Instead of test
we must replace it with ';alert(1);'
404 - 'File "'; // 404 - 'string' = NaN
alert(1); // pew pew pew
'" was not found in this folder.' // string is vaild JS
Great!
Now all in one
https://challenge.intigriti.io/#..//server-status/?%253Ciframe%2520srcdoc%253D%2527%253Cscript%2520src%253D%2522x%252527%253Balert(document.domain)%253B%252527%2522%253E%253C%252Fscript%253E%2527%253E%253C%252Fiframe%253E%250A
The challenge was super fun! I had fun (kudos Intigriti) it took me less than an hour to solve it out, I submitted my solution after that and it was confirmed. I hope you learned something and good luck with future challenges.