Post

SpookTastic

Challenge

  • CTF: Hack The Boo 2023 - Practice Official Writeup
  • Name: SpookTastic
  • Category: Web
  • Difficulty: Very Easy
  • Points: 325
  • Description: On a moonless night, you delve into the dark web to uncover the hacker group “The Cryptic Shadows.” You find an encrypted message guiding you to a web challenge. They claim a cursed amulet, the ‘Amulet of Samhain,’ can unveil their treasure’s location, hidden deep within the site’s code, protected by formidable security. Only those brave enough to pop an alert will gain access to the prized amulet.
  • Objective: Script Tag Ommited XSS

Files

Download: web_spooktastic.zip

Writeup

Lets spin-up the docker image of the web and setup our static-code analysis IDE:

1
2
code .
sudo ./build_docker.sh

Browsing to the local webpage: http://127.0.0.1:1337/, we are greeted with a spooky booking page: spooktastic_1

Scrolling to the bottom of the page we can subscribe to a newsletter that will send a request with our submitted email to /api/register: spooktastic_2

1
2
3
4
5
6
7
8
9
10
11
POST /api/register HTTP/1.1
Host: 127.0.0.1:1337
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: close
Content-Type: application/json
Content-Length: 25

{"email":"test@test.com"}

Sending a Cross-Site Scripting (XSS) payload such as <img src=x onerror=alert('xxs')> ends up triggered an alert of Thank you for signing up to our newsletter and then the flag also follows: HTB{f4k3_fl4g_f0r_t35t1ng}. This is because a bot ends up parsing all the emails and sending them the flag, however the payload needs to bypass the blacklist_pass function and doesn’t even check for basic email syntax:

challenge/app.py

1
2
3
4
5
6
7
def blacklist_pass(email):
    email = email.lower()

    if "script" in email:
        return False

    return True

The bot HTML page is parsed with an unsafe string escape via the |safe operator. This ends up sending the flag over a websocket connection:

templates/bot.html

1
2
3
{% for email in emails %}
    <span>{{ email|safe }}</span><br/>
{% endfor %}

Websocket data:

1
`42["flag",{"flag":"HTB{f4k3_fl4g_f0r_t35t1ng}\n"}]`

Spinning up a docker instance and sending the same payload, we can then obtain the real flag. Flag: HTB{4l3rt5_c4us3_jumpsc4r35!!}

This post is licensed under CC BY 4.0 by the author.