Spookifier Challenge Writeup — HackTheBoo CTF
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?
Website Landing Page:
Initial Testing:
Trying a random input:
Seems like it returns different fonts for whatever text I enter…
Code Review:
Analyzing routes.py:
The web application seems to be running the Mako template engine and there is only one route. The text parameter gets passed to a function called spookify()
and then the output is rendered in the template. Let’s check out util.py
since that is from where the spookify()
function is imported.
Analyzing util.py:
Let’s first check out the generate_render()
function:
This function takes the converted fonts after they get passed through spookify()
and it then puts them into a table for the web app user to see (after it gets rendered into the template).
Now let’s analyze thespookify()
and change_font()
functions:
The change_font()
function just returns a list with all four of the modified fonts of the original text. The spookify()
function calls change_font()
on the user-supplied text and saves that to a variable. It then calls the aforementioned generate_render()
function on that variable to create the table with the fonts that the user sees on the website.
Exploitation:
Mako Server-Side Template Injection:
Since there’s obviously no input validation at any point before it gets passed to the Mako template engine’s render_template()
function in routes.py
, the web app is vulnerable to Server-Side Template Injection (SSTI).
As can be seen, inputting ${7*7}
(Mako syntax) returns 49, which tells us that Mako is running our commands.
Interesting Tangent — Trying to run code with Mako’s python block syntax:
When trying to run <% import os; test=os.popen(‘id’).read() %> ${test}
(Mako syntax), this is what happens:
The fourth table (the other tables aren’t important) doesn’t render the <
character or the %
character. This is because the font dictionaries defined in util.py
tells the change_font
function what each character should be converted to. In this case, the fourth font dictionary (the other dictionaries aren’t important) hasn’t defined what <
and %
should be converted to, so they just get skipped over by change_font()
and therefore are not passed into render_template()
, which causes Mako to see the remaining text as just normal text.
Remote Command Execution using the Template Namespace:
We can access the os module from the Template Namespace (or self
), which allows us to run arbitrary commands:
Payload: ${self.module.cache.util.os.popen(“id”).read()}
This returned 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)
, which means that the id
command ran successfully!
Getting the flag:
Final payload: ${self.module.cache.util.os.popen(“cat ../flag.txt”).read()}
And there’s our flag!! The flag is: HTB{t3mpl4t3_1nj3ct10n_1s_$p00ky!!}
Thanks for Reading!
If this post was beneficial to you, consider dropping a follow and liking the post! Make sure to leave any comments or questions down below!
CTF Team Info:
If you would like to join my CTF team, then please visit my About Page, and you can find more information there!