Template Injection
Template engines are tools that allow developers / designers to separate programminglogic from the presentation of data when creating dynamic web pages. In other words,
rather than have code that receives an HTTP request, queries the necessary data from the
database and then presents it to the user in a monolithic file, template engines separate
the presentation of that data from the rest of the code which computes it (as an aside,
popular frameworks and content management systems also separate the HTTP request
from the query as well).
Server Side Template Injection (SSTI) occurs when those engines render user input
without properly sanitizing it, similiar to XSS. For example, Jinja2 is a templating language
for Python, and borrowing from nVisium, an example 404 error page might look like:
@app.errorhandler(404)
def page_not_found(e):
template = '''{%% extends "layout.html" %%}
{%% block body %%}
<div class="center-content error">
<h1>Opps! That page doesn't exist </h1>
<h3>%s</h3>
</div>{%% endblock %%}
''' % (request.url)return
render_template_string(template), 404
Source: (https://nvisium.com/blog/2016/03/09/exploring-ssti-in-flask-jinja2)
Here, the page_not_found function is rendering HTML and the developer is formatting
the URL as a string and displaying it to the user. So, if an attacker enters http://foo.com/nope{{7*7}},
the developers code would render http://foo.com/nope49, actually evaluating the
expression passed in. The severity of this increases when you pass in actual Python code
which Jinja2 will evaluate.
Now, the severity of each SSTI depends on the template engine being used and what, if
any, validation the site is performing on the field. For example, Jinja2 has been associated
with arbitrary file access and remote code execution, the Rails ERB template engine has
been associated with Remote Code Execution, Shopify’s Liquid Engine allowed access
to a limited number of Ruby methods, etc. Demonstrating the severity of your find will
really depend on testing out what is possible. And though you may be able to evaluate
some code, it may not be a significant vulnerability in the end. For example, I found an
SSTI by using the payload {{4+4}} which returned 8. However, when I used {{4*4}}, the
text {{44}} was returned because the asterisk was stripped out. The field also removed
special characters like () and [] and only allowed a maximum of 30 characters. All this
combined effectively rendered the SSTI useless.
In contrast to Server Side Template Injections are Client Side Template Injections. These
occur when applications using client side template frameworks, like AngularJS, embed
user content into web pages without sanitizing it. This is very similar to SSTI except it is
a client side framework which creates the vulnerability. Testing for CSTI with Angular is
similar to Jinja2 and involves using {{ }} with some expression inside.
Examples
1. Uber Angular Template Injection
Difficulty: High
Url: developer.uber.com
Report Link: https://hackerone.com/reports/125027 1
Date Reported: March 22, 2016
Bounty Paid: $3,000
Description:
In March 2016, James Kettle (one of the developers of Burp Suite, a tool recommended in
the Tools chapter) found a CSTI vulnerability with the URL https://developer.uber.com/docs/deep-
linking?q=wrtz{{7*7}} with the URL. According to his report, if you viewed the rendered
page source, the string wrtz49 would exist, demonstrating that the expression had been
evaluated.
Now, interestingly, Angular uses what is called sandboxing to “maintain a proper separa-
tion of application responsibilities”. Sometimes the separation provided by sandboxing is
designed as a security feature to limit what a potential attacker could access. However,
with regards to Angular, the documentation states that “this sandbox is not intended
to stop attacker who can edit the template� [and] it may be possible to run arbitrary
Javascript inside double-curly bindings�” And James managed to do just that.
arbitrary Javascript executed:
https://developer.uber.com/docs/deep-linking?q=wrtz{{(_="".sub).call.call({}[$="\
constructor"].getOwnPropertyDescriptor(_.__proto__,$).value,0,"alert(1)")()}}zzz\
As he notes, this vulnerability could be used to hijack developer accounts and associated
apps.
Takeaways
Be on the lookout for the use of AngularJS and test out fields using the Angular
syntax {{ }}. To make your life easier, get the Firefox plugin Wappalyzer - it will
show you what software a site is using, including the use of AngularJS.
2. Uber Template Injection
Difficulty: Medium
Url: riders.uber.com
Report Link: hackerone.com/reports/125980 2
Date Reported: March 25, 2016
Bounty Paid: $10,000
Description:
When Uber launched their public bug bounty program on HackerOne, they also included
a “treasure map” which can be found on their site, https://eng.uber.com/bug-bounty.
The map details a number of sensitive subdomains that Uber uses, including the
technologies relied on by each. So, with regards to the site in question, riders.uber.com,
the stack included Python Flask and NodeJS. So, with regards to this vulnerability, Orange
(the hacker) noted that Flask and Jinja2 were used and tested out the syntax in the name
field.
Now, during testing, Orange noted that any change to a profile on riders.uber.com results
in an email and text message to the account owner. So, according to his blog post, he
tested out {{1+1}} which resulted in the site parsing the expression and printing 2 in the
email to himself.
Next he tried the payload {% For c in [1,2,3]%} {{c,c,c}} {% endfor %} which runs a for
loop resulting in the following on the profile page:
As you can see, on the profile page, the actual text is rendered but the email actually
executed the code and injected it in the email. As a result, a vulnerability existing allowing
an attacker to execute Python code.
Now, Jinja2 does try to mitigate the damage by sandboxing the execution, meaning
the functionality is limited but this can occasionally be bypassed. This report was
originally supported by a blog post (which went up a little early) and included some great
links to nVisium.com’s blog (yes, the same nVisium that executed the Rails RCE) which
demonstrated how to escape the sandbox functionality:
• https://nvisium.com/blog/2016/03/09/exploring-ssti-in-flask-jinja2
• https://nvisium.com/blog/2016/03/11/exploring-ssti-in-flask-jinja2-part-ii
Takeaways
Take note of what technologies a site is using, these often lead to key insights
into how you can exploit a site. In this case, Flask and Jinja2 turned out to be
great attack vectors. And, as is the case with some of the XSS vulnerabilities,
the vulnerability may not be immediate or readily apparent, be sure to check all
places were the text is rendered. In this case, the profile name on Uber’s site
showed plain text and it was the email which actually revealed the vulnerability.