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.
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)
:
We can see that the filter used the following rules to neutralize our payload:
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:
Javascript code execution:
However when I tried to read the secret
variable, I got the following error in Dev Tools:
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: