Don’t let evil hackers abuse this simple Flask/Jinja2 mistake

Exploring Server-Side Template Injections through Hack the Box Web Challenge “Templated”

Roberto
InfoSec Write-ups

--

Summary

If you’re using Flask and Jinja2, don’t let hackers abuse one silly mistake that could give them access to remote code execution (RCE) on your server. Essentially, Jinja2 templating is pretty powerful and allows even complex expressions to be run, resulting in server-side template injection (SSTI). For example, the following expression rendered in Flask/Jinja2 on an HTML template will run the ls -a command on the server and render the results of this command on the screen. Explore the Hack the Box Web Challenge “Templated” to see an example of this in action.

{{request.application.__globals__.__builtins__.__import__(‘os’).popen(‘ls -a /’).read()}}

Fix: This is hard fix since there are many ways to bypass firewalls, some of which can be found on here on this OnSecurity article. The main issue is that there is some untrusted input/data that is rendering through the template, so one solution would be to simply not allow users to create these custom templates. However, there might be unavoidable cases where you need to allow this to occur. Looking at the Jinja2 documentation, one of the most best ways to protect from this untrusted input is sandboxing. This will help greatly protect against SSTI.

Screenshot from the Jinja2 documentation.

Hack the Box Web Challenge: Templated

I will be detailing my thought process and solution to the Hack the Box (HTB) web challenge: Templated, which examines the SSTI to RCE vulnerability.

AI-generated image by DALL-E mini on prompt “server side template injection.” https://www.craiyon.com/
AI-generated image by DALL-E mini on prompt “server side template injection.” https://www.craiyon.com/

How to Get Started:

Go to https://app.hackthebox.com/challenges . Under “Active Challenges,” click on “Web”. Look for “Templated” and then start the instance (if you don’t see it, it might be under “Retired Challenges” at the time of reading). When you start the instance, you should be able to see a host:port (i.e something like 157.245.46.136:30055). Copy the host into a new browser tab/window. Good to go!

Starting the “Templated” challenge
Starting the “Templated” challenge

Reconnaissance: Flask and Jinja2

Opening the site, we see that the app is “powered by Flask/Jinja2”. What exactly is Flask or Jinja2?

Flask is a popular web framework that is written in python. It provides different libraries and modules that allow developers to write their application framework in python (see full documentation here).

Jinja2 is a python-based templating engine that can be used in conjunction with Flask. What does a templating engine do? Essentially, it allows your HTML to render dynamic data that are stored as variables (see full documentation here). As shown in the example below from the documentation, a_variable is registered in the template within the double curly braces such as {{a_variable}} . When the template is rendered, whatever a_variable contains will be displayed.

Example from https://jinja.palletsprojects.com/en/3.1.x/templates/#synopsis<!DOCTYPE html>
<html lang="en">
<h1>My Webpage</h1>
{{ a_variable }}
</html>

There is really nothing going on the / page, so the first thing I tried was to search for a random page /index on the URL.

Adding /index to the end of the page

The 404 error for this index page renders “The page ‘index’ could not be found.” I retried with /invalid , and we again see the same message, but with the text ‘invalid’ instead of index.

‘invalid’ being rendered in the template

It’s clear that the text for the missing page is being rendered in plain text. Therefore, whatever is searched might potentially be rendered on the site. At this point, it seemed that the solution was most likely one of two things, either a Cross-Site Scripting Attack (XSS) or a Server-Side Template Injection (SSTI) exploit. Both of the links provide more in-detail explanation, but I’ll go into some quick summary of both. XSS is a form of injection attack in which you somehow manage to inject a script (i.e the ‘Scripting’) that is usually not on the victim’s site (i.e the ‘Cross-Site’) to do some malicious work. SSTI is a form of injection in which “templates” (which are rendered on the ‘Server-Side,” meaning they are processed on the server) handle user input in an unsafe manner, leading to some form of remote control execution (RCE).

I first tried XSS.

Injected HTML DOM elements

As seen in the screenshot above, it’s clear that the template string will be displayed literally as HTML, so I was able to add a small <b> tag to make the contents afterward bolded. The DOM is visibly modified as well.

However, in the URL, I was unable to get a working <script> or <img> tags to work properly, as inputing another / character for the closing tags in the URL would change how the page was being processed (result seen below). I did not try other more advanced XSS methods after this, so if you got this to work, comment below!

XSS with script tag not working

SSTI solution:

Server-Side Template Injection (SSTI) was the next option to try for me. The link provided has a great explanation to understand how to find targets and build an exploit (naming Jinja2 as a classic victim framework). Using Jinja2’s templating indicators, I added {{7*7}} (as suggested by the link) to the end of the URL.

Result from adding {{7*7}} to the end of Invalid. %7B = ‘{‘ and %7D = ‘}’ in URI encoding

As seen in the result, instead of invalid{{7*7}} being displayed, we see invalid49 , indicating that what on the inside of the template is being executed as an expression. Doing some more research, I came across this article that has a beautiful explanation of how to exploit SSTI specifically on Flask/Jinja2 frameworks, much like our problem here. If the following summary does not make sense, read the linked article.

Summary: In Python’s object model, there are certain types and modules that are built-in to the language that can be accessed globally. The goal is to gain access to the python OS module Method Resolution Order, which can be used to run arbitrary commands to the server (this could shut down the server, delete files, etc). The full Python documentation is here. In our case, we want to use the OS module to search through the server and find the file containing the flag.

Flask contains two important global objects that I used in trying to get the flag: config and request . I first tried to print the config and see if the flag was listed as a secret key (using invalid{{config}}. It was not unfortunately.

Config files for Flask app printed

Then, in order to access the built-in modules for OS, I used the request object to search for it, using: ‘{{request.application.__globals__.__builtins__.__import__(‘os’).popen(‘CMD’).read()}}’ as the template, where CMD is the terminal command to run of choice (if you’re unsure of what each thing means, then just start from the request object, and add one by one .application , .__globals__ , and so on).

Using the CMD ls (search ‘man ls’ to read more about ‘ls’) to look through the files, I got the result:

using `ls` on the server

We see that flag.txt is a file, and in order to read the content, you simply replace the ls CMD with cat flag.txt (search ‘man cat’ to read more about ‘cat’). This prints out the HTB flag! Success! Luckily, I didn’t have to run any other commands or other forms of the template since there was no firewall that sanitized the user input. We also had sufficient permissions to read the file, so no modifications had to be done there. See here for more info on that.

That should be it for this challenge. Thanks for reading through and please leave any constructive feedback, suggestions, or questions below! If you enjoyed, please consider following me on Medium. Contact me at roberto.cyberkid@gmail.com, follow me on twitter, and connect with me on LinkedIn!

From Infosec Writeups: A lot is coming up in the Infosec every day that it’s hard to keep up with. Join our weekly newsletter to get all the latest Infosec trends in the form of 5 articles, 4 Threads, 3 videos, 2 Github Repos and tools, and 1 job alert for FREE! https://weekly.infosecwriteups.com/

--

--

Stanford alum, Software Engineer with a passion for CyberSec, Biotech, and Sustainability. Work with me at https://www.tidallabs.io/.