What is server-side template injection(SSTI Vulnerability)?
A 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 concatenated directly into a
template, rather than passed in as data. This allows attackers to inject
arbitrary template directives in order to manipulate the template
engine, often enabling them to take complete control of the server.
An example of vulnerable code see the following one:
$output = $twig->render("Dear " . $_GET['name']);
In the previous example 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://example.com/?name={{bad-stuff-here}}
Constructing a server-side template injection attack
How To Detect SSTI Vulnerability
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 the polyglot ${{<%[%'”}}%.
In
order to check if the server is vulnerable you should spot the
differences between the response with regular data on the parameter and
the given payload.
If an error is thrown it will be quiet easy to figure out that
the server is vulnerable and even which engine is running. But you could
also find a vulnerable server if you were expecting it to reflect the
given payload and it is not being reflected or if there are some missing
chars in the response.
Detect – Plaintext context
The given input is being rendered and reflected into the response. This is easily mistaken for a simple XSS vulnerability, but it’s easy to differentiate if you try to set mathematical operations within a template expression:
{{7*7}}
${7*7}
<%= 7*7 %>
${{7*7}}
#{7*7}
Detect – Code context
In these cases the user input is being placed within a template expression:
engine.render("Hello {{"+greeting+"}}", data)
The URL access that page could be similar to: http://vulnerable-website.com/?greeting=data.username
If you change the greeting parameter for a different value the response won’t contain the username, but if you access something like: http://example.com/?greeting=data.username}}hello then, the response will contain the username (if the closing template expression chars were }}).
If an error is thrown during these test, it will be easier to find that the server is vulnerable.
Identify SSTI Vulnerability
Once you have detected the template injection potential, the next step is to identify the template engine.
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.
If you are lucky the server will be printing the errors and you will be able to find the engine used inside the errors. Some possible payloads that may cause errors:
${} | {{}} | <%= %> |
${7/0} | {{7/0}} | <%= 7/0 %> |
${foobar} | {{foobar}} | <%= foobar %> |
${7*7} | {{7*7}} | “ |
Otherwise, you’ll need to manually test different language-specific payloads and study how they are interpreted by the template engine. A common way of doing this is to inject arbitrary mathematical operations using syntax from different template engines. You can then observe whether they are successfully evaluated. To help with this process, you can use a decision tree similar to the following:
Exploition of SSTI Vulnerability
Read
The first step after finding template injection and identifying the template engine is to read the documentation. Key areas of interest are:
- ‘For Template Authors’ sections covering basic syntax.
- ‘Security Considerations’ – chances are whoever developed the app you’re testing didn’t read this, and it may contain some useful hints.
- Lists of builtin methods, functions, filters, and variables.
- Lists of extensions/plugins – some may be enabled by default.
Explore
Assuming no exploits have presented themselves, the next step is to explore the environment to find out exactly what you have access to. You can expect to find both default objects provided by the template engine, and application-specific objects passed in to the template by the developer. Many template systems expose a ‘self’ or namespace object containing everything in scope, and an idiomatic way to list an object’s attributes and methods.
If there’s no builtin self object you’re going to have to
bruteforce variable names using SecLists and Burp Intruder’s wordlist
collection.
Developer-supplied objects are particularly likely to contain
sensitive information, and may vary between different templates within
an application, so this process should ideally be applied to every
distinct template individually.
Attack
At this point you should have a firm idea of the attack surface available to you and be able to proceed with traditional security audit techniques, reviewing each function for exploitable vulnerabilities. It’s important to approach this in the context of the wider application – some functions can be used to exploit application-specific features. The examples to follow will use template injection to trigger arbitrary object creation, arbitrary file read/write, remote file include, information disclosure and privilege escalation vulnerabilities.