logo
    • Home
    • Categories
    • About
  • en-languageEnglish
SecurityBy Pierre Colart

Server-side Template Injection

Contexte

Server-side template injection occurs 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. Server-side template injection attacks can occur when user input is directly concatenated into a template, rather than being passed as data. This allows attackers to inject arbitrary template directives to manipulate the template engine, often allowing them to take full control of the server. As the name suggests, server-side template injection payloads are delivered and evaluated server-side, making them potentially much more dangerous than a typical client-side template injection.

Server-side template injection vulnerabilities can expose websites to various attacks depending on the template engine in question and exactly how the application uses it. In some rare circumstances, these vulnerabilities pose no real security risk. However, most of the time, the impact of server-side template injection can be catastrophic.

At the most severe end of the scale, an attacker can potentially achieve remote code execution, take full control of the main server, and use it to carry out further attacks on the internal infrastructure. Even in cases where complete remote code execution is not possible, an attacker can often use server-side template injection as a base for many other attacks, potentially gaining read access to sensitive data and arbitrary files on the server.

How

Les vulnérabilités liées à l'injection de modèles côté serveur surviennent lorsque les entrées utilisateur sont concaténées avec des modèles plutôt que d'être transmises sous forme de données. Les modèles statiques, qui fournissent simplement des espaces réservés pour afficher du contenu dynamique, ne sont généralement pas vulnérables à ce type d'attaque. Par exemple, un e-mail qui accueille chaque utilisateur par son nom, tel que celui-ci extrait d'un modèle Twig:

 $output = $twig->render("Hello {first_name},", array("first_name" => $user.first_name) );

Server-side Template Injection

In this section, we will discuss what server-side template injection is and describe the basic methodology for exploiting server-side template injection vulnerabilities. We will also suggest ways to ensure that your own use of templates does not expose you to server-side template injection.

If you already understand the basic concepts behind server-side template injection vulnerabilities and simply want to practice exploiting them on realistic and deliberately vulnerable targets, you can access all labs in this section from the link below.

View all Server-side Template Injection Labs This technique was first documented by PortSwigger in our 2015 research article on the subject. If you would like to know how we were able to exploit some of these vulnerabilities on live websites, a complete account is available on our research page.

Search Server-side Template Injection What is Server-side Template Injection? Server-side template injection occurs when an attacker is able to use the native template syntax to inject a malicious payload into a template, which is then executed on the server-side.

Template engines are designed to generate web pages by combining fixed templates with volatile data. Server-side template injection attacks can occur when user input is directly concatenated into a template, rather than passed as data. This allows attackers to inject arbitrary template directives to manipulate the template engine, often allowing them to take full control of the server. As the name implies, server-side template injection payloads are delivered and evaluated server-side, making them potentially much more dangerous than typical client-side template injection.

What is the impact of Server-side Template Injection? Server-side template injection vulnerabilities can expose websites to various attacks depending on the template engine in question and the exact way the application uses it. In some rare circumstances, these vulnerabilities pose no real security risk. However, most of the time, the impact of server-side template injection can be catastrophic.

At the most severe end of the scale, an attacker may potentially achieve remote code execution, take full control of the main server, and use it to perform other attacks on the internal infrastructure.

Even in cases where full remote code execution is not possible, an attacker can often use server-side template injection as a basis for many other attacks, potentially gaining read access to sensitive data and arbitrary files on the server.

How do Server-side Template Injection Vulnerabilities Occur? Server-side template injection vulnerabilities occur when user input is concatenated into templates instead of being passed as data.

Static templates that simply provide placeholders in which dynamic content is rendered are generally not vulnerable to server-side template injection. The classic example is an email that greets each user by name, like the following Twig template excerpt:

 $output = $twig->render("Hello{first_name},", array("first_name" => $user.first_name)); This code is not vulnerable to server-side template injection because the user's first name is simply passed to the template as data. It is not used to construct or modify the template in any way that would allow an attacker to inject malicious code.

However, since templates are simply strings, web developers sometimes directly concatenate user input into templates before rendering. Let's take a similar example to the one above, but this time users can customize certain parts of the email before sending it. For example, they can choose the name used:

 $output = $twig->render("Hello" . $_GET['name']);

In this example, instead of a static value being passed to the template, a part of the template itself is generated dynamically using the "name" parameter. Since the template syntax is evaluated server-side, this potentially allows an attacker to place a server-side template injection payload in the "name" parameter as follows:

 http://website.com/?name={{bad-code-here}}

Such vulnerabilities are sometimes caused by accidents due to poor template design by individuals who are not familiar with security implications. As in the above example, you can see different components, some of which contain user inputs, concatenated and integrated into a template. In some ways, this is similar to SQL injection vulnerabilities that occur in poorly written prepared statements.

However, this behavior is sometimes intentionally implemented. For example, some websites deliberately allow certain privileged users, such as content editors, to modify or submit custom templates from the outset. This clearly poses a huge security risk if an attacker is able to compromise an account with such privileges.

Building a Server-side Template Injection Attack

Identifying server-side template injection vulnerabilities and crafting a successful attack typically involves the following high-level process.

Detect

Server-side template injection vulnerabilities often go unnoticed, not because they are complex, but because they are only really apparent to auditors who are explicitly looking for them. If you are able to detect the presence of a vulnerability, it can be surprisingly easy to exploit. This is especially true in sandbox-less environments.

As with any vulnerability, the first step towards exploitation is being able to find it. The simplest initial approach may be to try fuzzing the template by injecting a sequence of commonly used special characters in template expressions, such as ${{<%[%'"}}%. If an exception is thrown, it indicates that the injected template syntax is potentially being interpreted by the server in some way. This is a sign that a server-side template injection vulnerability may exist.

Server-side template injection vulnerabilities occur in two distinct contexts, each requiring its own detection method. Regardless of the results of your fuzzing attempts, it is important to also try the following contextual approaches. If fuzzing was not conclusive, a vulnerability may still be revealed using one of these approaches. Even if fuzzing suggested a template injection vulnerability, you still need to identify its context in order to exploit it.

Plain Text Context

Most template languages allow you to freely input content either by directly using HTML tags or by using the native template syntax, which will be rendered in HTML format on the back-end before the HTTP response is sent.

This can sometimes be exploited for XSS and is actually often mistaken for a simple XSS vulnerability. However, by setting mathematical operations as the parameter value, we can test whether this is also a potential entry point for a server-side template injection attack.

Let's take the example of a template containing the following vulnerable code:

 render('Hello ' + username)

During the audit, we can test for server-side template injection by requesting a URL such as:

 http://website.com/?username=${7*7}

If the resulting output contains "Hello 49", this indicates that the mathematical operation is being evaluated server-side. This is a good proof of concept for a server-side template injection vulnerability.

Note that the specific syntax required to successfully evaluate the mathematical operation varies depending on the template engine being used.

Code Context

In other cases, the vulnerability is exposed by user input placed in a template expression, as we saw earlier with our email example. This can take the form of a user-controlled variable name placed inside a parameter, such as:

 greeting = getQueryParameter('greeting')
engine.render("Hello {{"+greeting+"}}", data)

On the website, the resulting URL would look like:

 http://website.com/?greeting=data.username

This would be rendered in the output as "Hello Carlos," for example.

This context easily goes unnoticed during evaluation, as it does not result in obvious XSS and is almost indistinguishable from a simple hashmap lookup. One testing method for server-side template injection in this context is to first establish that the parameter does not contain direct XSS vulnerability by injecting arbitrary HTML code in the value:

 http://website.com/?greeting=data.username<tag>

In the absence of XSS, this will typically result in either an empty entry in the output (just "Hello" without a username), encoded tags, or an error message. The next step is to try to break out of the statement using a common template syntax and attempt to inject arbitrary HTML code after it:

 http://vulnerable-website.com/?greeting=data.username}}<tag>

If this again results in an error or empty output, you either used the wrong template language syntax or, if no template-style syntax seems to be valid, server-side template injection is not possible. Alternatively, if the output is rendered correctly, with the arbitrary HTML, this is a key indication that a server-side template injection vulnerability is present:

 Hello Carlos<tag>

Identity

Once you have detected the potential for model injection, the next step is to identify the model engine.

Although there are a large number of model languages, many of them use a very similar syntax that is specifically chosen not to conflict with HTML characters. Therefore, it can be relatively simple to create poll payloads to test which model engine is being used.

Simply submitting invalid syntax is often enough because the resulting error message will tell you exactly what the model engine is, and sometimes even what version. For example, the invalid expression <%=foobar%> triggers the following response from the ERB engine based on Ruby:

 (erb):1:in `<main>': undefined local variable or method `foobar' for main:Object (NameError)
from /usr/lib/ruby/2.5.0/erb.rb:876:in `eval'
from /usr/lib/ruby/2.5.0/erb.rb:876:in `result'
from -e:4:in `<main>'

Otherwise, you will need to manually test different language-specific payloads and study how they are interpreted by the model engine. By using a process of elimination based on whether the syntax appears to be valid or invalid, you can narrow down the options faster than you might think. A common approach is to inject arbitrary mathematical operations using the syntax of different model engines and observe whether they are successfully evaluated. To facilitate this process, you can use a decision tree similar to this one:

It's important to note that the same payload can sometimes return a positive response in multiple model languages. For example, the payload {{7*'7'}} returns 49 in Twig and 7777777 in Jinja2. Therefore, it's important not to jump to conclusions based on a single successful response.

How to prevent vulnerabilities

The best way to prevent server-side model injection is to not allow any user to modify or submit new models. However, this is sometimes unavoidable due to business requirements.

One of the simplest ways to avoid introducing server-side model injection vulnerabilities is to always use a "logicless" model engine, such as Mustache, except when absolutely necessary. Separating the logic from the presentation as much as possible can significantly reduce your exposure to the most dangerous model-based attacks.

Another measure is to only execute user code in a sandbox environment where potentially dangerous modules and functions have been completely removed. Unfortunately, sandboxing unreliable code is inherently difficult and subject to circumvention.

Finally, another complementary approach is to accept that the execution of arbitrary code is almost inevitable and to apply your own sandboxing by deploying your model environment in a locked Docker container, for example.

Pierre Colart

Passionate developer and architect who wants to share their world and discoveries in order to make things simpler for everyone.

See profil

Latest posts

Sequences, Time Series and Prediction

© 2023 Switch case. Made with by Pierre Colart